买个正点原子linux板子用,发现屏幕和正点原子stm32的屏幕不通用,另购买一块屏幕还是贵,思考如何通过不购买屏幕也可以体验它到显示。可以通过一根网线来解决
解决思路,linux开发板运行应用程序,使用网线将的数据通过网线传至笔记本电脑的屏幕,在window界面编写程序将图像显示出来。
所需工具:笔记本一台,一根网线,正点原子linux开发板
1.linux程序
1.打开linux的显存,将显存映射到到一个地址,通过此地址可以修改和观察显存数据,通过udp协议将数据传送至电脑端
编译使用虚拟机交叉编译,具体方法参考正点原子手册学习
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#define SERVER_PORT 8888 //定义端口号:(0-1024为保留端口号,最好不要用)
#define MAX_BUF_SIZE 1024*3
long ceshi ;
int ceshi1 = 0;
char buffer[MAX_BUF_SIZE];
char buffer1[MAX_BUF_SIZE];
#define display_width 1024
#define display_high 600
#define send_size 50+4
int sockfd; //socket描述符
struct sockaddr_in addr; //定义服务器起地址
void set_adress(int adress,unsigned char *temp)//adress 代表显存下标,buffer表传输的数值
{
temp[0] = adress >>24;
temp[1] = adress >>16;
temp[2] = adress >>8;
temp[3] = adress;
temp[0] = 55;
}
void send_date(int temp,int color) //只发送一个颜色
{
int n;
int i;
int repeat = 0;
int len;
struct sockaddr *appaddr;
len = sizeof(struct sockaddr_in);
//while(1)
{ /* 从键盘读入,写到服务端 */
#if 0
printf("Please input char:\n");
fgets(buffer1,MAX_BUF_SIZE,stdin);
#endif
i = 0;
//temp = y*display_width+x;
buffer[i] = (unsigned char)(temp/1000000%100);
i++;
buffer[i] = (unsigned char)(temp/10000%100);
i++;
buffer[i] = (unsigned char)(temp/100%100);
i++;
buffer[i] = (unsigned char)(temp%100);
i++;
buffer[i] = (unsigned char )((color>>8));
i++;
buffer[i] = (unsigned char )((color));
i++;
buffer[i] = 0;
appaddr = (struct sockaddr *)&addr;
sendto(sockfd,buffer,10,0,(struct sockaddr*)appaddr,len);
bzero(buffer,MAX_BUF_SIZE);
}
}
void send_date_1(int temp,int length,unsigned short *p) //发送一串颜色1024×2 个长度,使用2个字节作为行数
{
int n;
int i;
int j;
int k = 0;
int repeat = 0;
int color;
int len;
struct sockaddr *appaddr;
len = sizeof(struct sockaddr_in);
//while(1)
{ /* 从键盘读入,写到服务端 */
#if 0
printf("Please input char:\n");
fgets(buffer1,MAX_BUF_SIZE,stdin);
#endif
i = 0; //地址从0开始
//temp = ceshi++;
j = temp;
buffer[i] = (unsigned char)(temp/1000000%100);
i++;
buffer[i] = (unsigned char)(temp/10000%100);
i++;
buffer[i] = (unsigned char)(temp/100%100);
i++;
buffer[i] = (unsigned char)(temp%100);
i++;
buffer[i] = (unsigned char)(length>>8);
i++;
buffer[i] = (unsigned char)(length&0xff);
i++;
for(k = 0;k < length;k++)
{
color = p[j++];
buffer[i++] = (unsigned char )((color>>8));
buffer[i++] = (unsigned char )((color));
}
//buffer[i++] = 0;//结束符号
appaddr = (struct sockaddr *)&addr;
sendto(sockfd,buffer,i,0,(struct sockaddr*)appaddr,len);
//bzero(buffer,MAX_BUF_SIZE);
//sleep(1);
}
}
void draw_point(int x,int y,int color)
{
int n;
int i;
int repeat = 0;
int temp;
int len;
struct sockaddr *appaddr;
len = sizeof(struct sockaddr_in);
//while(1)
{ /* 从键盘读入,写到服务端 */
#if 0
printf("Please input char:\n");
fgets(buffer1,MAX_BUF_SIZE,stdin);
#endif
i = 0;
temp = y*display_width+x;
buffer[i] = (unsigned char)(temp/1000000%100);
buffer[i]+= 1;
i++;
buffer[i] = (unsigned char)(temp/10000%100);
buffer[i]+= 1;
i++;
buffer[i] = (unsigned char)(temp/100%100);
buffer[i]+= 1;
i++;
buffer[i] = (unsigned char)(temp%100);
buffer[i]+= 1;
i++;
buffer[i] = (unsigned char )((color>>16)+1);
i++;
buffer[i] = (unsigned char )((color>>8)+1);
i++;
buffer[i] = (unsigned char )((color)+1);
i++;
buffer[i] = 0;
appaddr = (struct sockaddr *)&addr;
sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)appaddr,len);
bzero(buffer,MAX_BUF_SIZE);
}
}
void dra_line(int x1,int y1,int x2,int y2,int color)
{
int i;
for(i = x1;i<x2;i++)
draw_point(i,y1,color);
}
void dra_rct(int x1,int y1,int x2,int y2,int color)//k代表是横还是竖
{
int i ;
for(i = x1;i< x2;i++)
{
draw_point(i,y1,color);
draw_point(i,y2,color);
}
for(i=y1;i<y2;i++)
{
draw_point(x1,i,color);
draw_point(x2,i,color);
}
}
void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len)
{
int n;
int i;
int repeat = 0;
while(1)
{ /* 从键盘读入,写到服务端 */
#if 0
printf("Please input char:\n");
fgets(buffer1,MAX_BUF_SIZE,stdin);
#endif
i = 0;
#if 0
buffer[i] = (unsigned char)(ceshi/1000000%100);
buffer[i]+= 1;
i++;
buffer[i] = (unsigned char)(ceshi/10000%100);
buffer[i]+= 1;
i++;
buffer[i] = (unsigned char)(ceshi/100%100);
buffer[i]+= 1;
i++;
buffer[i] = (unsigned char)(ceshi%100);
buffer[i]+= 1;
i++;
buffer[i] = ceshi1;
i++;
buffer[i] = ceshi1;
i++;
buffer[i] = ceshi1;
i++;
buffer[i] = 0;
#endif
dra_rct(50,50,200,200,ceshi);
sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)addr,len);
bzero(buffer,MAX_BUF_SIZE);
sleep(1);
}
}
int main(int argc,char **argv)
{
int i,j,k;
struct fb_fix_screeninfo fb_fix;
struct fb_var_screeninfo fb_var;
unsigned int screen_size;
int fd;
int temp = 0;
static int width; //LCD X分辨率
static int height; //LCD Y分辨率
unsigned short *screen_base = NULL; //映射后的显存基地址
struct sockaddr *appaddr;
int len;
len = sizeof(struct sockaddr_in);
if(argc!=2)
{
fprintf(stderr,"Usage:%s server_ip\n",argv[0]);
exit(1);
}
/* 建立 sockfd描述符 */
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
fprintf(stderr,"Socket Error:%s\n",strerror(errno));
exit(1);
}
/* 打开framebuffer设备 */
if (0 > (fd = open("/dev/fb0", O_RDWR))) {
perror("open error");
exit(EXIT_FAILURE);
}
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
screen_size = fb_fix.line_length * fb_var.yres;
width = fb_var.xres;
height = fb_var.yres;
fprintf(stderr,"width = %d,height = %d,size_int = %d\n",width,height,sizeof(short));
//printf("width = %d,height = %d\n",width,height);
screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == (void *)screen_base) {
perror("mmap error");
close(fd);
exit(EXIT_FAILURE);
}
/* 填充服务端的资料 */
bzero(&addr,sizeof(struct sockaddr_in)); // 初始化,置0
addr.sin_family=AF_INET; // Internet
addr.sin_port=htons(SERVER_PORT);// (将本机器上的short数据转化为网络上的short数据)端口号
if(inet_aton(argv[1],&addr.sin_addr)<0) /*inet_aton函数用于把字符串型的IP地址转化成网络2进制数字*/
{
fprintf(stderr,"Ip error:%s\n",strerror(errno));
exit(1);
}
if (0 > (fd = open("/dev/fb0", O_RDWR)))
{
//perror("open error");
fprintf(stderr,"open error:%s\n",strerror(errno));
exit(EXIT_FAILURE);
}
//udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in)); //
i = 0;
while(1)
{
int changdu = 300;//长度为像素点长度,不是待发送数组的长度;
if(i >= width*height)
{
changdu = width*height - i;
send_date_1(i,changdu,screen_base);
i = 0;//重新开始
}
else
{
send_date_1(i, changdu, screen_base);
}
i += changdu;
}
close(sockfd);
}
2.window程序
此程序以默认地址IPaddr[18] ="192.168.10.200",端口为8888,此处需要将以太网的ipv4地址固定,防止每次都要修改ip,
程序
// linux_screen.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "linux_screen.h"
#include <WINSOCK2.H>
#include <string>
#include <winsock2.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib ")
#define MAX_LOADSTRING 100
#define max_size 1024*3
// 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
int sta,stb,stc,std1;
static int i = 0;
LPCWSTR sum2[20];
int ceshi;
int port = 8888;
char IPaddr[18] ="192.168.10.200";
int ceshi1,ceshi2,ceshi3,ceshi4;
#define dis_high 600
#define dis_width 1024
#define dlt_x 300
#define dlt_y 700
unsigned short display[dis_high*dis_width]={0};
unsigned short display_1[dis_high*dis_width]={0};//之前的屏幕,为减少刷新
unsigned long position = 0;
unsigned int pisition_num = 0;
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
bool BindSocket(SOCKET& socket,short port ,const char* Ip) //网络函数
{
SOCKADDR_IN address;
address.sin_family=AF_INET;
address.sin_addr.s_addr =inet_addr(Ip);
address.sin_port=htons(port);
memset(address.sin_zero,0,sizeof(address.sin_zero));
if(SOCKET_ERROR == bind(socket,(const struct sockaddr*)&address,sizeof(struct sockaddr)) )
{
return false;
}
return true;
}
DWORD WINAPI Fun(LPVOID lpParamter)//第二条线程
{
WORD wVersionRequested;
WSADATA wData; // 这结构是用于接收Wjndows Socket的结构信息的
int err;
SOCKADDR_IN address;
int n =sizeof(struct sockaddr);
char talk[100]={0};
char buffer[max_size] = {0};
int i = 0;
int j = 0;
wVersionRequested = MAKEWORD( 1, 1 ); // 请求WinSock库
err = WSAStartup( wVersionRequested, &wData );
if ( err != 0 ) {
return -1; // 返回值为零时表示成功WSAStartup
}
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
if(sockSrv<0)
{
printf("failed");
return 0;
}
#if 0
printf("NEWSocket success.\n");
printf("请输入本机IP地址:\n");
gets(IPaddr);
printf("请输入本机端口号:\n");
scanf("%d",&port);
gets(buffer); //去掉回车
#endif
while(! BindSocket(sockSrv,port,IPaddr) )
{
#if 0
printf("failed");
printf("请输入本机端口号:\n");
scanf("%d",&port);
#endif
}
printf("BindSocket success.\n");
while(1)
{
i = recvfrom(sockSrv,buffer, max_size,0,(struct sockaddr*)&address,&n);
if(i ==SOCKET_ERROR )
printf("接收失败\n");
else
{
//printf("%d:\n",i);
pisition_num = i;
position = 0;
#if 0
position = (buffer[0]);
position *= 100;
position += (buffer[1]);
position *= 100;
position +=( buffer[2]);
position *= 100;
position += (buffer[3]);
display[position] = buffer[4];
display[position] <<=8;
display[position] += buffer[5];
#endif
#if 1
position = (buffer[0]);
position *= 100;
position += (buffer[1]);
position *= 100;
position +=( buffer[2]);
position *= 100;
position += (buffer[3]);
int length;
length = (buffer[4]);
length <<= 8;
length += (buffer[5]);
int i;
int j;
for(i = 6,j = 0;j < length;j++)
{
display[position+j] = buffer[i++];
display[position+j] <<=8;
display[position+j] += buffer[i++];
}
#endif
ceshi1 = length;
ceshi2 = buffer[1];
ceshi3 = buffer[2];
ceshi4 = buffer[3];
//buffer[i-1] = '\0';
//puts(buffer);
//printf("please talk:\n");
//gets(talk);
//sendto(sockSrv,talk, sizeof(talk), 0,(const struct sockaddr*)&address,sizeof(struct sockaddr) );
}
}
closesocket(sockSrv);
WSACleanup();
}
int co16_to_24(int temp)
{
int temp_1 = 0;
temp_1 = ((temp&0xf800)<<8);
temp_1 |= ((temp&0x7e0)<<5);
temp_1 |= ((temp&0x1f)<<3);
return temp_1;
}
void totary(HDC hdc)
{
HPEN hPen;
HPEN hPen1;
int i;
int j;
int k = 0;
unsigned char temp = 0;
//hPen = CreatePen (PS_DOT, 3, 0xff) ;
//hPen1 = CreatePen (PS_DOT, 3, 0) ;
//SelectObject (hdc, hPen) ;
//MoveToEx (hdc, 300, 300, NULL) ;
//LineTo (hdc, 300, 300) ;
//DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN))) ;
//HBITMAP hbit = CreateBitmap(1024,600,8,24,display);CreateCompatibleBitmap
#if 0
HBITMAP hBitmap = CreateBitmap(1024,600,1,24,display);
if(hBitmap)
{
BITMAP bitmap;
//GetObject (hBitmap, sizeof (BITMAP), &bitmap) ;
HDC hdcMem = CreateCompatibleDC (hdc) ;
CreateCompatibleBitmap(hdcMem,1024,600);
SelectObject (hdcMem, hBitmap) ;
BitBlt(hdc,dlt_x,dlt_y,1024,600,hdcMem,0,0,SRCINVERT);
DeleteDC (hdcMem) ;
}
#endif
BITMAP bit = {0, 200, 600, 4, 1, 1};
//bit.bmBitsPixel=16;
//bit.bmType = 0;
//bit.bmHeight = 600;
//bit.bmWidth = 1024;
//bit.bmWidthBytes = 16;
#if 0
bit.bmBits = display;
HBITMAP hBitmap = CreateBitmapIndirect(&bit);
SetBitmapBits(hBitmap,sizeof(display),display);
GetObject (hBitmap, sizeof (BITMAP), &bit) ;
HDC hdcMem = CreateCompatibleDC (hdc) ;
SelectObject (hdcMem, hBitmap) ;
BitBlt(hdc,dlt_x,dlt_y,20,600,hdcMem,0,0,SRCCOPY);
DeleteDC (hdcMem) ;
#endif
#if 0
tagBITMAPINFOHEADER head;
head.biSize = 40;
head.biWidth = 1024;
head.biHeight = 600;
head.biPlanes = 1;
head.biBitCount = 4;
head.biCompression = BI_RGB;
#endif
#if 1
BITMAPINFO info;
info.bmiHeader.biSize = 40;
info.bmiHeader.biWidth = 1024;
info.bmiHeader.biHeight = 600;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 16;
//info.bmiHeader.biCompression = BI_RGB;
info.bmiHeader.biCompression = 0;
//info.bmiColors = 0x00332211;
info.bmiColors[0].rgbBlue = 255;
info.bmiColors[0].rgbGreen = 255;
info.bmiColors[0].rgbRed = 255;
HDC hdcMem = CreateCompatibleDC (hdc) ;
StretchDIBits(hdc,
dlt_x,dlt_y,
1024,-600,
0,0,
1024,600,
display,
&info,
DIB_RGB_COLORS,
SRCCOPY);
#endif
#if 0
for(j=0;j<dis_high;j++)
{
for(i=0;i<dis_width;i++)
{
//if(display[j*dis_width+i] != display_1[j*dis_width+i])
{
display_1[j*dis_width+i] = display[j*dis_width+i];
//ceshi = display_1[j*dis_width+i];
//if(2 == k)
{
SetPixel (hdc, i+dlt_x, j+dlt_y, co16_to_24(display[j*dis_width+i])) ;
k = 0;
}
//else
{
k ++;
}
//StretchBits
//FloodFill();
}
}
}
#endif
TextOut (hdc, 50, 50,(LPCWSTR)sum2,wsprintf ((LPWSTR)sum2, TEXT ("%i"),ceshi1)) ;
//TextOut (hdc, 50, 80,(LPCWSTR)sum2,wsprintf ((LPWSTR)sum2, TEXT ("%i"),(ceshi2))) ;
//TextOut (hdc, 50, 100,(LPCWSTR)sum2,wsprintf ((LPWSTR)sum2, TEXT ("%i"),ceshi3)) ;
//TextOut (hdc, 50, 120,(LPCWSTR)sum2,wsprintf ((LPWSTR)sum2, TEXT ("%i"),ceshi4)) ;
}
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable;
int i;
for(i = 0;i<dis_width*dis_high;i++)
display[i]= 0x5;
HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
CloseHandle(hThread);
// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_LINUX_SCREEN, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_LINUX_SCREEN));
// 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
// 注释:
//
// 仅当希望
// 此代码与添加到 Windows 95 中的“RegisterClassEx”
// 函数之前的 Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要,
// 这样应用程序就可以获得关联的
// “格式正确的”小图标。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LINUX_SCREEN));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_LINUX_SCREEN);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // 将实例句柄存储在全局变量中
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
0, 0, 1920, 1080, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
totary(hdc);
InvalidateRect (hWnd, NULL, 0) ;
#if 0
ceshi+=200;
for(i = 0;i< 1024*600;i++)
{
display[i] = ceshi;
}
#endif
EndPaint (hWnd, &ps) ;
break;
// TODO: 在此添加任意绘图代码...
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
3.linux的shell脚本,设置linux开发板的ip并运行生成的linux程序,此处192.168.10.200对应window的ip地址
运行前需要确认window和linux开发板通讯是否异常,本人测试linux开发板平window不通原因为window的防火墙未关闭,
#!/bin/sh
chmod 777 uartapp
ifconfig eth0 up
ifconfig eth0 192.168.10.205
echo set ok
./uartapp 192.168.10.200
4.最终效果,图像折叠可能问设备树的参数不正确,不影响实际显示,实测途中图标可以被鼠标点击,时钟会走动