-
1.前言帧缓冲框架是Linux下专门为显示类设备设计的接口,目的是将硬件和软件层分离开,方便应用层的编程,也方便应用层程序移植。帧缓冲框架向驱动层和应用层分别提供了一套标准接口,驱动层按照框架编写驱动,应用层按照框架编写应用程序。帧缓冲在/dev目录下生成的标准节点是fb,比如:/dev/fb0,/dev/fb1等等。这篇文章就介绍在应用层 如何利用帧缓冲框架接口封装LCD屏的画点函数,获取LCD屏的硬件信息,完成对LCD屏编程,实现文字、数字显示。当期的文字采用点阵方式取模来完成显示,比较简单,与单片机上的LCD编程思路一样,可以更方便快速学习帧缓冲编程。后续正常开发中一般采用矢量字库完成字体显示,大小调整方便,字体更换方法,在前面文章有介绍过嵌入式Linux如何交叉编译freetype库。2. 编程思路下面是帧缓冲框架图:帧缓冲设备是标准的字符设备,通过open函数打开设备,再通过ioctl接口获取LCD屏的一些硬件参数信息,在利用mmap函数映射LCD屏的地址到应用层。映射的这个地址就相当于是LCD屏的显存地址,对这个地址里写入数据就可以在LCD屏硬件上实时显示出来。int fd=open("/dev/fb0",O_RDWR); if(fd<0) { perror("设备文件打开失败"); return 0; } /*1. 获取LCD屏的可变形参*/ ioctl(fd,FBIOGET_VSCREENINFO,&var); printf("分辨率:%d*Ú688a51d-fb1d-4e4a-8041-87e875247737n",var.xres,var.yres); printf("像素点位数:Ú688a51d-fb1d-4e4a-8041-87e875247737n",var.bits_per_pixel); /*2. 获取LCD屏的固定形参*/ ioctl(fd,FBIOGET_FSCREENINFO,&fix); printf("映射的长度:Ú688a51d-fb1d-4e4a-8041-87e875247737n",fix.smem_len); printf("一行的字节数:Ú688a51d-fb1d-4e4a-8041-87e875247737n",fix.line_length); /*3. 映射LCD缓冲区地址到进程空间*/ fb_mem=mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(fb_mem==NULL) { perror("空间映射失败!\n"); return 0; } 在应用mmap函数将驱动的DMA缓冲区地址映射到进程空间之后,如何控制LCD呢? 为了方便对LCD屏进行操作,需要封装一个画点函数。然后后续的图片显示,文字显示,其他图形显示都基于这个画点函数来完成,程序就很好设计。当前采用的LCD屏是800*480分辨率,24位像素,通过这些参数就可以编写一个公式,封装画点函数。下面是封装好的函数原型:fb_mem是LCD屏映射的地址,后面的参数是获取的LCD屏硬件参数信息。/* 函数功能: 画点 */ void Show_Pixel(int x,int y,int color) { unsigned int *lcd=(unsigned int *)(fb_mem+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8); *lcd=color; //颜色赋值 }有了画点函数,就可以封装中文显示函数: 下面这个函数是针对横向取模的点阵字模进行描点的,其他取模方式根据情况修改即可。/* 函数功能: 显示中文 说明: 取模的字体是按照横向取模进行取点阵码。 取模的字体宽度是8的倍数。 */ void ShowFont(int x,int y,int size,unsigned char *data) { int i,j,x0=x; unsigned char tmp; for(i=0;i<size/8*size;i++) { tmp=data[i]; for(j=0;j<8;j++) { if(tmp&0x80)Show_Pixel(x0,y,0xFF0033); //else 画背景色 x0++; tmp<<=1; } if(x0-x==size) { y++; x0=x; } } }中文可以显示,照着思路再封装一个字母数字的显示接口:/* 函数功能: 显示一个ASCII码。“空格开始 到 ~ 结束” !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~ 说明: ASCII码取模 宽度和高度必须是8的倍数,传入的size(高) */ void ShowASCII(int x,int y,int size,unsigned char c_data) { int i,j,x0=x; unsigned char data; int cnt; if(c_data<' ' || c_data > '~')return; cnt=c_data-' '; for(i=0;i<size/2/8*size;i++) { switch(size) { case 16: break; case 32: data=ASCII_16_32[cnt][i]; break; } for(j=0;j<8;j++) { if(data&0x80)Show_Pixel(x0,y,0xFF0033); x0++; data<<=1; } if(x0-x==size/2) { x0=x; y++; } } }3. 完整示例代码3.1 中文显示下面这份代码是单个汉字取模进行显示的,代码较少,整体比较简单。#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/fb.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <string.h> struct fb_var_screeninfo var; //可变参数 struct fb_fix_screeninfo fix; //固定参数 unsigned char *fb_mem=NULL; //LCD屏的首地址 extern unsigned char font[]; /* 函数功能: 画点 */ void Show_Pixel(int x,int y,int color) { unsigned int *lcd=(unsigned int *)(fb_mem+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8); *lcd=color; //颜色赋值 } /* 函数功能: 显示中文 说明: 取模的字体是按照横向取模进行取点阵码。 取模的字体宽度是8的倍数。 */ void ShowFont(int x,int y,int size,unsigned char *data) { int i,j,x0=x; unsigned char tmp; for(i=0;i<size/8*size;i++) { tmp=data[i]; for(j=0;j<8;j++) { if(tmp&0x80)Show_Pixel(x0,y,0xFF0033); //else 画背景色 x0++; tmp<<=1; } if(x0-x==size) { y++; x0=x; } } } int main(int argc,char **argv) { int fd=open("/dev/fb0",O_RDWR); if(fd<0) { perror("设备文件打开失败"); return 0; } /*1. 获取LCD屏的可变形参*/ ioctl(fd,FBIOGET_VSCREENINFO,&var); printf("分辨率:%d*Ú688a51d-fb1d-4e4a-8041-87e875247737n",var.xres,var.yres); printf("像素点位数:Ú688a51d-fb1d-4e4a-8041-87e875247737n",var.bits_per_pixel); /*2. 获取LCD屏的固定形参*/ ioctl(fd,FBIOGET_FSCREENINFO,&fix); printf("映射的长度:Ú688a51d-fb1d-4e4a-8041-87e875247737n",fix.smem_len); printf("一行的字节数:Ú688a51d-fb1d-4e4a-8041-87e875247737n",fix.line_length); /*3. 映射LCD缓冲区地址到进程空间*/ fb_mem=mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(fb_mem==NULL) { perror("空间映射失败!\n"); return 0; } /*4. 控制显示屏*/ memset(fb_mem,0xFFFFFF,fix.smem_len); //将屏幕清屏为白色 ShowFont(100+0*56,100,56,font); ShowFont(100+1*56,100,56,font+7*56*1); ShowFont(100+2*56,100,56,font+7*56*2); munmap(fb_mem,fix.smem_len); close(fd); return 0; } unsigned char font[]= { /*-- 文字: 嵌 --*/ /*-- 幼圆42; 此字体下对应的点阵为:宽x高=56x56 --*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38, 0x00,0x00,0x00,0x03,0x80,0x00,0x7C,0x00,0x03,0x80,0x07,0xC0,0x00,0x7C,0x00,0x03, 0xC0,0x07,0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07,0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07, 0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07,0xC0,0x00,0x7C,0x00,0x03,0xC0,0x07,0xC0,0x00, 0x7C,0x00,0x03,0xC0,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x03,0xFF,0xFF,0xFF,0xFF, 0xFF,0x80,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0xFF,0xFF,0xFF,0xFF,0xFC,0x00, 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0xF0,0x00,0x00,0x01,0xE0, 0x0F,0x01,0xF0,0x00,0x00,0x01,0xE0,0x0F,0x01,0xF0,0x00,0x00,0x01,0xE0,0x0F,0x01, 0xE0,0x00,0x00,0x01,0xE0,0x0F,0x01,0xE0,0x00,0x00,0x1F,0xFF,0xFF,0xF3,0xFF,0xFF, 0xF0,0x3F,0xFF,0xFF,0xFB,0xFF,0xFF,0xF8,0x3F,0xFF,0xFF,0xFB,0xFF,0xFF,0xF8,0x1F, 0xFF,0xFF,0xE7,0xC3,0x80,0x7C,0x01,0xE0,0x0F,0x07,0x87,0x80,0x7C,0x01,0xE0,0x0F, 0x0F,0x87,0x80,0x7C,0x01,0xE0,0x0F,0x0F,0x87,0x80,0x78,0x01,0xE0,0x0F,0x1F,0x07, 0x80,0x78,0x01,0xE0,0x0F,0x1F,0x07,0x80,0xF8,0x01,0xE0,0x0F,0x3E,0x07,0x80,0xF8, 0x01,0xE0,0x0F,0x3E,0x07,0x80,0xF0,0x01,0xE0,0x0F,0x3C,0x07,0x81,0xF0,0x01,0xE0, 0x0F,0x3C,0x07,0x81,0xE0,0x01,0xE0,0x0F,0x00,0x07,0x83,0xE0,0x01,0xFF,0xFF,0x00, 0x07,0xC3,0xC0,0x01,0xFF,0xFF,0x00,0x07,0xC0,0x00,0x01,0xFF,0xFF,0x00,0x0F,0xE0, 0x00,0x01,0xFF,0xFF,0x00,0x0F,0xE0,0x00,0x01,0xE0,0x0F,0x00,0x0F,0xF0,0x00,0x01, 0xE0,0x0F,0x00,0x1E,0xF0,0x00,0x01,0xE0,0x0F,0x00,0x1E,0xF8,0x00,0x01,0xE0,0x0F, 0x00,0x3E,0x78,0x00,0x01,0xE0,0x0F,0x00,0x7C,0x7C,0x00,0x01,0xE0,0x0F,0x00,0x78, 0x3E,0x00,0x01,0xE0,0x0F,0x00,0xF8,0x1E,0x00,0x01,0xE0,0x0F,0x01,0xF0,0x1F,0x00, 0x01,0xE0,0x0F,0x03,0xE0,0x0F,0x80,0x01,0xE0,0x0F,0x07,0xE0,0x07,0xC0,0x01,0xE0, 0x0F,0x0F,0xC0,0x03,0xE0,0x01,0xE0,0x0F,0x1F,0x80,0x01,0xF0,0x01,0xFF,0xFF,0x3F, 0x00,0x01,0xF8,0x00,0xFF,0xFF,0x7E,0x00,0x00,0xFC,0x00,0x7F,0xFE,0x78,0x00,0x00, 0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x18, /*-- 文字: 入 --*/ /*-- 幼圆42; 此字体下对应的点阵为:宽x高=56x56 --*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xC0,0x00,0x00, 0x00,0x00,0x00,0x0F,0xE0,0x00,0x00,0x00,0x00,0x00,0x07,0xF0,0x00,0x00,0x00,0x00, 0x00,0x01,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x00,0x00,0x00, 0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00, 0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00, 0x00,0x00,0x00,0x3F,0x80,0x00,0x00,0x00,0x00,0x00,0x3F,0x80,0x00,0x00,0x00,0x00, 0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x7B, 0xC0,0x00,0x00,0x00,0x00,0x00,0xFB,0xE0,0x00,0x00,0x00,0x00,0x00,0xF1,0xE0,0x00, 0x00,0x00,0x00,0x00,0xF1,0xE0,0x00,0x00,0x00,0x00,0x01,0xF1,0xF0,0x00,0x00,0x00, 0x00,0x01,0xE0,0xF0,0x00,0x00,0x00,0x00,0x03,0xE0,0xF8,0x00,0x00,0x00,0x00,0x03, 0xC0,0xF8,0x00,0x00,0x00,0x00,0x07,0xC0,0x7C,0x00,0x00,0x00,0x00,0x07,0x80,0x7C, 0x00,0x00,0x00,0x00,0x0F,0x80,0x3C,0x00,0x00,0x00,0x00,0x0F,0x00,0x3E,0x00,0x00, 0x00,0x00,0x1F,0x00,0x1E,0x00,0x00,0x00,0x00,0x1E,0x00,0x1F,0x00,0x00,0x00,0x00, 0x3E,0x00,0x0F,0x00,0x00,0x00,0x00,0x7C,0x00,0x0F,0x80,0x00,0x00,0x00,0x7C,0x00, 0x07,0xC0,0x00,0x00,0x00,0xF8,0x00,0x07,0xC0,0x00,0x00,0x01,0xF0,0x00,0x03,0xE0, 0x00,0x00,0x01,0xE0,0x00,0x01,0xE0,0x00,0x00,0x03,0xE0,0x00,0x01,0xF0,0x00,0x00, 0x07,0xC0,0x00,0x00,0xF8,0x00,0x00,0x0F,0x80,0x00,0x00,0xF8,0x00,0x00,0x1F,0x00, 0x00,0x00,0x7C,0x00,0x00,0x1F,0x00,0x00,0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x00, 0x3E,0x00,0x00,0x7C,0x00,0x00,0x00,0x1F,0x00,0x00,0xF8,0x00,0x00,0x00,0x0F,0x80, 0x01,0xF0,0x00,0x00,0x00,0x07,0xC0,0x03,0xE0,0x00,0x00,0x00,0x03,0xE0,0x07,0xC0, 0x00,0x00,0x00,0x03,0xF0,0x0F,0x80,0x00,0x00,0x00,0x01,0xF8,0x1F,0x00,0x00,0x00, 0x00,0x00,0xFC,0x1E,0x00,0x00,0x00,0x00,0x00,0x7C,0x1C,0x00,0x00,0x00,0x00,0x00, 0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*-- 文字: 式 --*/ /*-- 幼圆42; 此字体下对应的点阵为:宽x高=56x56 --*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x06,0x00,0x00,0x00,0x00,0x00,0x00,0xEF,0x80,0x00,0x00,0x00,0x00,0x01,0xEF,0xE0, 0x00,0x00,0x00,0x00,0x01,0xEF,0xF0,0x00,0x00,0x00,0x00,0x01,0xE3,0xFC,0x00,0x00, 0x00,0x00,0x01,0xE0,0xFF,0x00,0x00,0x00,0x00,0x01,0xF0,0x7F,0x80,0x00,0x00,0x00, 0x01,0xF0,0x1F,0xC0,0x00,0x00,0x00,0x00,0xF0,0x07,0xC0,0x00,0x00,0x00,0x00,0xF0, 0x03,0xC0,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8, 0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF8,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00, 0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, 0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00, 0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, 0x00,0x00,0x00,0x7C,0x00,0x00,0x0F,0xFF,0xFF,0xFE,0x3C,0x00,0x00,0x0F,0xFF,0xFF, 0xFE,0x3C,0x00,0x00,0x0F,0xFF,0xFF,0xFE,0x3C,0x00,0x00,0x00,0x00,0xF0,0x00,0x3C, 0x00,0x00,0x00,0x00,0xF0,0x00,0x3E,0x00,0x00,0x00,0x00,0xF0,0x00,0x3E,0x00,0x00, 0x00,0x00,0xF0,0x00,0x1E,0x00,0x00,0x00,0x00,0xF0,0x00,0x1E,0x00,0x00,0x00,0x00, 0xF0,0x00,0x1E,0x00,0x00,0x00,0x00,0xF0,0x00,0x1F,0x00,0x00,0x00,0x00,0xF0,0x00, 0x0F,0x00,0x00,0x00,0x00,0xF0,0x00,0x0F,0x00,0x00,0x00,0x00,0xF0,0x00,0x0F,0x80, 0x00,0x00,0x00,0xF0,0x00,0x0F,0x80,0x18,0x00,0x00,0xF0,0x00,0x07,0x80,0x3C,0x00, 0x00,0xF0,0x00,0x07,0x80,0x3C,0x00,0x00,0xF0,0x00,0x07,0xC0,0x3C,0x00,0x00,0xF0, 0x00,0x03,0xC0,0x3C,0x00,0x00,0xF0,0x03,0xC3,0xE0,0x3C,0x00,0x00,0xF0,0x1F,0xC1, 0xE0,0x3C,0x00,0x00,0xF1,0xFF,0xC1,0xF0,0x3C,0x00,0x00,0xFF,0xFF,0x01,0xF0,0x3C, 0x00,0x01,0xFF,0xF8,0x00,0xF8,0x3C,0x00,0x0F,0xFF,0x80,0x00,0xF8,0x7C,0x01,0xFF, 0xFC,0x00,0x00,0x7C,0x7C,0x1F,0xFF,0xC0,0x00,0x00,0x3F,0x78,0x3F,0xFC,0x00,0x00, 0x00,0x1F,0xF8,0x3F,0xC0,0x00,0x00,0x00,0x1F,0xF0,0x00,0x00,0x00,0x00,0x00,0x07, 0xF0,0x00,0x00,0x00,0x00,0x00,0x01,0xE0 };3.2 调用字库显示单个汉字取模太麻烦,可以使用点阵字库取模软件,然后程序里打开字库,读取字模进行绘制,下面的示例代码就是采用字库读取字模完成汉字显示。#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/fb.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> struct fb_var_screeninfo var; //可变参数 struct fb_fix_screeninfo fix; //固定参数 unsigned char *fb_mem=NULL; //LCD屏的首地址 unsigned char *fb_GBK=NULL; //GBK字库的首地址 extern unsigned char font[]; extern const unsigned char ASCII_16_32[95][64]; /* 函数功能: 画点 */ void Show_Pixel(int x,int y,int color) { unsigned int *lcd=(unsigned int *)(fb_mem+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8); *lcd=color; //颜色赋值 } /* 函数功能: 显示中文 说明: 取模的字体是按照横向取模进行取点阵码。 取模的字体宽度是8的倍数。 */ void ShowFont(int x,int y,int size,unsigned char *font) { unsigned char L,H; unsigned int Addr; unsigned int font_size=size*size/8; //得到该字节的总字节大小 unsigned char FontData[128]; //最大可以存放32*32尺寸字体点阵码 unsigned char *GBK_p; //存放汉字点阵码首地址 /*计算该汉字在字库里的地址偏移量*/ L=*(font+1); //低字节 H=*font; //高字节 if(L<0x7f)L=L-0x40; else L=L-0x41; H=H-0x81; Addr=(190*H+L)*font_size; //得到当前汉字在字库里的偏移量 GBK_p=(unsigned char *)(Addr+fb_GBK); //得到汉字点阵码在字库里首地址 int i,j,x0=x; unsigned char tmp; for(i=0;i<size/8*size;i++) { tmp=GBK_p[i]; for(j=0;j<8;j++) { if(tmp&0x80)Show_Pixel(x0,y,0xFF0033); //else 画背景色 x0++; tmp<<=1; } if(x0-x==size) { y++; x0=x; } } } /* 函数功能: 显示一个ASCII码。“空格开始 到 ~ 结束” !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 说明: ASCII码取模 宽度和高度必须是8的倍数,传入的size(高) */ void ShowASCII(int x,int y,int size,unsigned char c_data) { int i,j,x0=x; unsigned char data; int cnt; if(c_data<' ' || c_data > '~')return; cnt=c_data-' '; for(i=0;i<size/2/8*size;i++) { switch(size) { case 16: break; case 32: data=ASCII_16_32[cnt][i]; break; } for(j=0;j<8;j++) { if(data&0x80)Show_Pixel(x0,y,0xFF0033); x0++; data<<=1; } if(x0-x==size/2) { x0=x; y++; } } } int main(int argc,char **argv) { if(argc!=2) { printf("./app <GBK字库文件>\n"); return 0; } int fd=open("/dev/fb0",O_RDWR); if(fd<0) { perror("设备文件打开失败"); return 0; } /*1. 获取LCD屏的可变形参*/ ioctl(fd,FBIOGET_VSCREENINFO,&var); printf("分辨率:%d*Ú688a51d-fb1d-4e4a-8041-87e875247737n",var.xres,var.yres); printf("像素点位数:Ú688a51d-fb1d-4e4a-8041-87e875247737n",var.bits_per_pixel); /*2. 获取LCD屏的固定形参*/ ioctl(fd,FBIOGET_FSCREENINFO,&fix); printf("映射的长度:Ú688a51d-fb1d-4e4a-8041-87e875247737n",fix.smem_len); printf("一行的字节数:Ú688a51d-fb1d-4e4a-8041-87e875247737n",fix.line_length); /*3. 映射LCD缓冲区地址到进程空间*/ fb_mem=mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(fb_mem==NULL) { perror("空间映射失败!\n"); return 0; } /*4. 映射GBK字库文件到进程空间*/ int gbk_fb; struct stat gbk_buf; gbk_fb=open(argv[1],2); if(gbk_fb<0) { perror("GBK字库文件打开失败!"); return 0; } stat(argv[1],&gbk_buf); printf("GBK字库文件大小=Ú688a51d-fb1d-4e4a-8041-87e875247737n",gbk_buf.st_size); fb_GBK=mmap(NULL,gbk_buf.st_size,PROT_READ,MAP_SHARED,gbk_fb,0); if(fb_GBK==NULL) { perror("空间映射失败!\n"); return 0; } /*4. 控制显示屏*/ memset(fb_mem,0xFFFFFF,fix.smem_len); //将屏幕清屏为白色 ShowFont(100+0*32,100,32,"嵌"); ShowFont(100+1*32,100,32,"入"); ShowFont(100+2*32,100,32,"式"); ShowFont(100+3*32,100,32,"开"); ShowFont(100+4*32,100,32,"发"); ShowASCII(100+0*16,200,32,'A'); ShowASCII(100+1*16,200,32,'C'); ShowASCII(100+2*16,200,32,'B'); munmap(fb_mem,fix.smem_len); close(fd); return 0; }
-
1. 前言上篇文章介绍了根文件系统的制作与NFS网络挂载,这篇文章介绍内核如何从本地挂载根文件系统,完成系统启动。本地挂载一般用在产品发布的时候,本地挂载的操作也分为两种。第一种: 在PC机上制作好文件映像rootfs.img,然后利用uboot加载直接烧写到EMMC里。这种最方便,适合产品批量生产烧录。需要依赖官方的工具。当前用的开发板是友善之臂的Tiny4412,官方提供了分区工具,可以将SD卡分区,将制作好的内核、uboot、文件系统映像、配置脚本放在SD卡里,然后开发板选择SD卡启动,就可以通过uboot完成EMMC分区,文件系统格式化、文件系统解压拷贝过程,在LCD屏上还有执行的进度条。第二种: 自己在uboot命令行对EMMC进行分区,然后拷贝文件系统到指定分区,再设置uboot环境变量完成挂载。这种可以了解整个挂载流程,对于学习来讲,非常适合。下面分别介绍两种挂载方式的执行过程。2. 本地挂载方式1这种放需要准备一张SD卡,然后需要利用官方光盘里提供的分区工具,对SD卡进行分区,再将内核、uboot、文件系统映像文件拷贝到SD卡上,再修改配置文件,设置启动的系统为Linux系统。根文件系统映像制作需要用到make_ext4fs命令,这个工具在光盘里也有,是一个压缩包,解压到PC机Linux系统下就可以使用。制作完成之后,将根文件系统拷贝到SD卡的image文件夹,与烧写安卓系统一样进行烧写。说明:其中的zImage文件是可以自己编译替换掉的。配置内核: # cp tiny4412_linux_defconfig .config制作根文件系统的方式: make_ext4fs -s -l <文件系统映像的大小> <生成的映像文件名称> <根文件系统存放的目录>示例: make_ext4fs -s -l 500M rootfs.img rootfs如果不知道rootfs文件系统的大小,可以使用du命令进行查看。 例如:du rootfs -h注意:在制作根文件系统映像包的时候,需要先将rootfs/dev目录下的所有文件全部删除掉。注意: 运行make_ext4fs命令需要使用超级用户身份,否则会导致文件系统制作之后无法加载。如果是普通用户身份登录系统,在执行命令时,加上sudo即可。示例: sudo make_ext4fs -s -l 500M rootfs.img rootfs [wbyq@wbyq work]$ sudo linux_tools/local/bin/make_ext4fs -s -l 500M rootfs.img rootfs/ [sudo] password for wbyq: Creating filesystem with parameters: Size: 524288000 Block size: 4096 Blocks per group: 32768 Inodes per group: 8000 Inode size: 256 Journal blocks: 2000 Label: Blocks: 128000 Block groups: 4 Reserved block group size: 31 Created filesystem with 5534/32000 inodes and 51924/128000 blocks3. 本地挂载方式2在完成当前介绍的挂载方式2,需要先完成NFS网络挂载才可以继续,大致的思路就是:(1)先将uboot、内核拷贝到开发板EMMC(2)进入到uboot命令行,对EMMC进行分区,格式化文件系统。(3)配置uboot环境变量,设置文件挂载挂载为NFS网络挂载,最后成功进入到命令行终端,然后将某个EMMC分区挂载到文件系统目录下,将根文件系统压缩包解压到这个目录,最后重启开发板进入到uboot命令行再次修改环境变量,设置uboot从刚才解压文件系统的分区启动,重启开发板,最后完成挂载。接下来就详细的完成上面说的步骤:(1)对EMMC进行分区安装文件系统 在uboot命令行执行以下代码。注意: 当前是从SD卡启动。 TINY4412 # fdisk -c 1 1024 1024 1024 TINY4412 # ext2format mmc 1:2(2)打包PC机的rootfs文件系统 [wbyq@wbyq work]$ sudo tar cvf rootfs.tar rootfs [wbyq@wbyq work]$ mv rootfs.tar rootfs/(3)使用NFS方式挂载解压文件系统到EMMC指定分区 以下命令是挂载NFS文件系统之后,进入到Linux命令行执行。注意: 当前是从SD卡启动 [root@wbyq ]# rm /dev/* [root@wbyq ]# mdev -s [root@wbyq ]# mount /dev/mmcblk1p2 /mnt/ [root@wbyq ]# cd /mnt/ [root@wbyq mnt]# tar xvf /rootfs.tar [root@wbyq mnt]# mv rootfs/* ./ [root@wbyq ]# umount /mnt/(4)切换成EMMC方式启动设置UBOOT环境变量 以下命令在UBOOT命令行执行。 TINY4412 # setenv bootargs root=/dev/mmcblk0p2 rootfstype=ext2 init=/linuxrc console=ttySAC0 lcd=S702 TINY4412 # saveenv TINY4412 # reset(5)本地方式下,使用NFS方式挂载NFS服务器的目录实现文件共享[root@wbyq ]# ifconfig eth0 192.168.10.123 [root@wbyq ]# mount -t nfs -o nolock 192.168.10.11:/home/wbyq/project把PC机文件拷贝到开发板上的方式:(1). SD卡或者U盘拷贝 (2). NFS网络方式 (3). 串口传输
-
1. 前言根文件系统是Linux内核启动之后挂载的第一个文件系统,上篇文章里已经介绍过,如何使用busybox来制作根文件系统。这篇文章介绍根文件系统制作成功后,如何让内核找到文件系统,并完成挂载,进入到系统命令行终端。根文件系统支持从网络挂载和本地挂载两种方式:(1)如果是采用网络挂载就将路径设置为/dev/nfs,NFS是Linux下常用的网络文件系统,这种方式挂载一般是在系统开发阶段,方便修改调试代码,也就是将根文件系统放在PC机上,嵌入式开发板通过网络到PC机进行访问。(2)本地挂载就是让内核从SD卡、EMMC、光盘等设备里加载根文件系统,设备路径设置为/dev/sda、/dev/ram等等。具体设备节点名称是什么就填什么。本地挂载一般用在产品发布的时候,当整个系统开发完毕,就将PC机上的完整目录拷贝到本地存储设备里,让内核直接从本地加载,这样开启启动的速度会快很多,文件读写也快很多,不在经过网络传输。本地挂载不方便调试代码,一般开发阶段还是NFS方式比较方便。上面说的路径设置这些配置是在uboot里设置,uboot有一个环境变量专门用于给内核传参,当内核启动之后,会根据uboot传递的参数完成执行。当前采用的开发板是友善之臂的Tiny4412,内核、uboot、交叉编译器都采用官方光盘自带的文件。如果设置内核为NFS网络挂载,uboot的环境变量设置格式如下: TINY4412 # setenv bootargs root=/dev/nfs nfsroot=192.168.10.11:/home/wbyq/work/rootfs ip=192.168.10.123:192.168.10.11:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0 lcd=S702 TINY4412 # saveenv TINY4412 # reset2. NFS网络方式挂载前提是先把根文件系统制作好才能继续这一步。当前的宿主机采用redhat6.3,这个系统比较老,但是功能是很齐全的,运行起来很流畅。要进行NFS网络挂载,需要先把PC机的网络与开发板的网络设置好,保证在同一个局域网内。当前的redhat6.3系统是跑在VM虚拟机里,接下来就对网络进行配置。开发板NFS网络挂载的思路:PC机开启NFS服务器,配置NFS共享的路径: 将rootfs共享出来.开发板当做NFS客户端,去挂载PC机共享的目录,完成文件系统的访问(1)配置虚拟机网络(2)配置PC机NFS服务器 [wbyq@wbyq rootfs]$ sudo vim /etc/exports /home/wbyq/work/rootfs *(insecure,rw,no_root_squash,sync)编写个脚本方便启动NFS服务器: 可以放在用户目录下 比如: nfs_restart.sh 运用脚本要使用管理员权限,脚本改权限为可执行 service nfs restart #重启 NFS 服务器 service iptables stop #关闭防火墙 ifconfig eth0 192.168.10.11 #设置IP地址查看系统网卡名称: ifconfig(3)运行脚本启动NFS服务器 [wbyq@wbyq ~]$ sudo ./nfs_restart.sh [sudo] password for wbyq: 关闭 NFS 守护进程: [失败] 关闭 NFS mountd: [失败] 关闭 NFS quotas: [失败] 关闭 NFS 服务: [确定] 启动 NFS 服务: [确定] 关掉 NFS 配额: [确定] 启动 NFS mountd: [确定] 正在启动 RPC idmapd: [确定] 正在启动 RPC idmapd: [确定] 启动 NFS 守护进程: [确定] [wbyq@wbyq ~]$ sudo ./nfs_restart.sh 关闭 NFS 守护进程: [确定] 关闭 NFS mountd: [确定] 关闭 NFS quotas: [确定] 关闭 NFS 服务: [确定] 启动 NFS 服务: [确定] 关掉 NFS 配额: [确定] 启动 NFS mountd: [确定] 正在启动 RPC idmapd: [确定] 正在启动 RPC idmapd: [确定] 启动 NFS 守护进程: [确定] [wbyq@wbyq ~]$ (4)设置UBOOT的环境变量 进入到uboot的命令行,设置环境变量。 set bootargs root=/dev/nfs nfsroot=192.168.10.11:/home/wbyq/work/rootfs ip=192.168.10.123:192.168.10.11:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0 lcd=S702 set bootargs root=/dev/nfs nfsroot=<服务器地址>:<NFS共享路径> ip=<开发板IP>:<服务器地址>:<网关>:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0 lcd=S702 执行示例: TINY4412 # set bootargs root=/dev/nfs nfsroot=192.168.10.11:/home/wbyq/work/rootfs ip=192.168.10.123:192.168.10.11:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0 lcd=S702 TINY4412 # save TINY4412 # reset重启之后开发板正常就会进入到Linux命令行:
-
1. 前言U-Boot 是一个主要用于嵌入式系统的引导加载程序,可以支持多种不同的计算机系统结构。U-Boot的命令为用户提供了交互功能,并且已经实现了几十个常用的命令,前面两篇文章介绍了uboot自带的常用命令使用。如果开发板需要很特殊的操作,可以添加新的U-Boot命令。U-Boot的每一个命令都是通过U_Boot_CMD宏定义的。这个宏在<include/command.h>头文件中定义。 #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \ cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}参数介绍: name:命令的名字,他不是一个字符串,不能用双引号括起来 maxargs:最大的参数个数 command:对应的函数指针 usage:一个字符串,简短的使用说明 help:一个字符串,比较详细的使用说明 UBOOT命令文件命名规则:cmd_xxx.c将写好的命令.c文件放入UBOOT源码顶层的/common目录下,并且修改Makefile文件,将加入的.c文件添加到编译选项中。在Makefile文件大约50行出进行添加即可: 格式:COBJS-y += cmd_xxx.o添加完毕,回到uboot顶层目录下,重新生成u-boot.bin文件,再下载到开发板测试。2. 自定义UBOOT命令代码2.1 编写蜂鸣器控制命令 #include <common.h> #include <command.h> #define GPD0CON (*(volatile unsigned int *)0x114000A0) //定义蜂鸣器IO口的地址 #define GPD0DAT (*(volatile unsigned int *)0x114000A4) int do_beep( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { GPD0CON&=~(0xf<<0); GPD0CON|=(0x1<<0); if(!strcmp(argv[1],"on")) //strcmp是比较字符串的函数,如果传进来的是on,就打开蜂鸣器 { GPD0DAT|=(1<<0); } if(!strcmp(argv[1],"off"))//strcmp是比较字符串的函数,如果传进来的是off,就关闭蜂鸣器 { GPD0DAT&=~(1<<0); } else printf("Usage:beep <on|off>!\n"); //如果不是on 也不是off 就输出提示 } U_BOOT_CMD( beep, //在u-boot命令行里显示的命令名称 2, //形参最大个数 1, //重复次数 (按下回车--自动执行上一次命令) do_beep, //命令执行函数(回调函数--) "传参格式: beep <on|off>", //用法提示 "传承示例:beep on 或者 beep off......." //帮助命令的提示信息 );2.2 编写LED灯控制命令 #include <common.h> #include <command.h> /* 1、LED灯接口配置寄存器 */ #define GPM4CON (*(volatile unsigned int *)0x110002E0) #define GPM4DAT (*(volatile unsigned int *)0x110002E4) int do_led(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { // led 1 on // led 1 off GPM4CON &= ~(0xf << 0 * 4); //清除寄存器1 GPM4CON |= (1 << 0 * 4); //输出模式 GPM4CON &= ~(0xf << 1 * 4); //清除寄存器2 GPM4CON |= (1 << 1 * 4); //输出模式 GPM4CON &= ~(0xf << 2 * 4); //清除寄存器3 GPM4CON |= (1 << 2 * 4); //输出模式 GPM4CON &= ~(0xf << 3 * 4); //清除寄存器4 GPM4CON |= (1 << 3 * 4); //输出模式 /*第一盏灯*/ if(!strcmp(argv[1],"1")) //strcmp是比较字符串的函数,如果传进来的是on,就打开蜂鸣器 { if(!strcmp(argv[2],"on")) { GPM4DAT &= ~(1 << 0); //点亮第一个灯 } else if(!strcmp(argv[2],"off")) { GPM4DAT |=1 << 0; //关闭第一个灯 } } /*第二盏灯*/ else if(!strcmp(argv[1],"2")) //strcmp是比较字符串的函数,如果传进来的是on,就打开蜂鸣器 { if(!strcmp(argv[2],"on")) { GPM4DAT &= ~(1 << 1); //点亮第二个灯 } else if(!strcmp(argv[2],"off")) { GPM4DAT |=1 << 1; //关闭第二个灯 } } /*第三盏灯*/ else if(!strcmp(argv[1],"3")) //strcmp是比较字符串的函数,如果传进来的是on,就打开蜂鸣器 { if(!strcmp(argv[2],"on")) { GPM4DAT &= ~(1 << 2); //点亮第三个灯 } else if(!strcmp(argv[2],"off")) { GPM4DAT |=1 << 2; //关闭第三个灯 } } /*第四盏灯*/ else if(!strcmp(argv[1],"4")) //strcmp是比较字符串的函数,如果传进来的是on,就打开蜂鸣器 { if(!strcmp(argv[2],"on")) { GPM4DAT &= ~(1 << 3); //点亮第四个灯 } else if(!strcmp(argv[2],"off")) { GPM4DAT |=1 << 3; //关闭第四个灯 } } else printf("Usage:led <1~4> <on|off>\n"); //如果不是on 也不是off 就输出提示 } U_BOOT_CMD( led, //在u-boot命令行里显示的命令名称 3, //形参最大个数 1, //重复次数 do_led, //命令执行函数 "user: LED count <on|off>", //用法提示 "cmd : (1)led 1 on (2)led 1 off...." //帮助命令的提示信息 );2.3 设计自己的movi命令 #include <common.h> #include <command.h> #include <environment.h> #include <linux/stddef.h> #include <malloc.h> #include <nand.h> #include <onenand_uboot.h> #include <mmc.h> #include <asm/arch/cpu.h> #include <asm/arch/movi_partition.h> int do_mymovi(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int r_cnt,w_cnt; /*1. 查找设备0-SD卡*/ struct mmc *mmc0 = find_mmc_device(0); if(mmc0==NULL) { printf("设备0查找失败!\n"); return 0; } /*2. 查找设备1--MMC*/ struct mmc *mmc1 = find_mmc_device(1); if(mmc1==NULL) { printf("设备1查找失败!\n"); return 0; } /*3. 初始化SD卡*/ mmc_init(mmc0); /*设备0初始化--SD卡*/ /*4. 初始化EMMC*/ mmc_init(mmc1); /*设备1初始化--EMMC卡*/ emmc_boot_open(mmc1); /*设备1打开---EMMC*/ /*5. 烧写数据*/ /*5.1 BL1*/ r_cnt=movi_read(0,1,16,(void*)0x40008000); //读出SD卡里存放到所有数据到DDR指定地址 w_cnt=movi_write(1,0,16,(void*)0x40008000);//将读出的数据写入到EMMC printf("BL1_r_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",r_cnt); printf("BL1_w_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",w_cnt); /*5.2 BL2*/ r_cnt=movi_read(0,17,32,(void*)0x40008000); //读出SD卡里存放到所有数据到DDR指定地址 w_cnt=movi_write(1,16,32,(void*)0x40008000);//将读出的数据写入到EMMC printf("BL2_r_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",r_cnt); printf("BL2_w_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",w_cnt); /*5.3 UBOOT\这里最好使用malloc申请空间,太大的地址可能会被其他数据覆盖掉*/ r_cnt=movi_read(0,49,656,(void*)0x40008000); //读出SD卡里存放到所有数据到DDR指定地址 w_cnt=movi_write(1,48,656,(void*)0x40008000);//将读出的数据写入到EMMC printf("UBOOT_r_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",r_cnt); printf("UBOOT_w_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",w_cnt); /*5.4 TZSW*/ r_cnt=movi_read(0,705,320,(void*)0x40008000); //读出SD卡里存放到所有数据到DDR指定地址 w_cnt=movi_write(1,704,320,(void*)0x40008000);//将读出的数据写入到EMMC printf("TZSW_r_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",r_cnt); printf("TZSW_w_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",w_cnt); /*5.5 Linux内核*/ r_cnt=movi_read(0,1057,12288,(void*)0x40008000); //读出SD卡里存放到所有数据到DDR指定地址 w_cnt=movi_write(1,1057,12288,(void*)0x40008000);//将读出的数据写入到EMMC printf("Linux内核_r_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",r_cnt); printf("Linux内核_w_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",w_cnt); emmc_boot_close(mmc1); //关闭EMMC /*5.5 环境变量*/ r_cnt=movi_read(0,1025,32,(void*)0x40008000); //读出SD卡里存放到所有数据到DDR指定地址 w_cnt=movi_write(1,1025,32,(void*)0x40008000);//将读出的数据写入到EMMC printf("环境变量_r_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",r_cnt); printf("环境变量_w_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",w_cnt); printf("环境变量拷贝成功!\n"); return 0; } U_BOOT_CMD( mymovi, /*命令的名称*/ 1, /*形参的最大个数*/ 0, /*命令执行重复次数*/ do_mymovi,/*命令处理函数*/ "将SD卡的BL1/BL2/uboot/签名文件/内核拷贝到EMMC", /*简短提示*/ "\n" "将SD卡的BL1/BL2/uboot/签名文件/内核拷贝到EMMC\n" /*完整提示*/ "注意: 该命令在开发板以SD卡启动方式时运用\n" );2.4 设计环境变量拷贝命令 #include <common.h> #include <command.h> #include <environment.h> #include <linux/stddef.h> #include <malloc.h> #include <nand.h> #include <onenand_uboot.h> #include <mmc.h> #include <asm/arch/cpu.h> #include <asm/arch/movi_partition.h> /* //以MMC方式启动,运行下面命令即可完成环境变量拷贝(SD-->EMMC) mmc read 1 40000000 401 20 mmc write 0 40000000 401 20 //以SD方式启动,运行下面命令即可完成环境变量拷贝 (SD--->EMMC) mmc read 0 40000000 401 20 mmc write 1 40000000 401 20 */ int do_copyenv(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int r_cnt,w_cnt; mmc_init(find_mmc_device(0)); /*设备0初始化--SD卡*/ mmc_init(find_mmc_device(1)); /*设备1初始化--EMMC卡*/ /*5.5 环境变量*/ r_cnt=movi_read(0,1025,32,(void*)0x40000000); //读出SD卡里存放到所有数据到DDR指定地址 w_cnt=movi_write(1,1025,32,(void*)0x40000000);//将读出的数据写入到EMMC printf("环境变量_r_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",r_cnt); printf("环境变量_w_cnt=Ôd971434-5b4d-4d5b-9798-8c60e95c71ffn",w_cnt); printf("环境变量拷贝成功!\n"); return 0; } U_BOOT_CMD( copyenv, /*命令的名称*/ 1, /*形参的最大个数*/ 0, /*命令执行重复次数*/ do_copyenv,/*命令处理函数*/ "将SD卡的环境变量拷贝到EMMC", /*简短提示*/ "\n" "将SD卡的环境变量拷贝到EMMC\n" /*完整提示*/ "注意: 该命令在开发板以SD卡启动方式时运用\n" );
-
1. 前言这篇文章是UBOOT命令介绍的下篇,接着上篇文章介绍剩下的几个命令用法。主要是涉及的命令是:磁盘分区、磁盘文件加载、内核引导、二进制文件加载、跳转命令、磁盘文件系统格式等等。2. UBOOT命令2.1 fatls –列出指定目录下的文件查看帮助: TINY4412 # ? fatls fatls - list files in a directory (default /) 列出一个目录文件 Usage: fatls <interface> <dev[:part]> [directory] - list files from 'dev' on 'interface' in a 'directory'参数说明: <interface>: mmc 或 usb; dev: 设备编号; part: 设备分区号; [directory]: 目录, 是可选, 可以不写,不写默认 / 目录查看SD卡中的文件列表(查看之前SD需要有完好的分区才行,可以通过fdisk进行分区,从U-BOOT和内核地址之后开始分区,防止将U-BOOT和内核清除) TINY4412 # fatls mmc 0 / Partition1: Start Address(0x71c53a), Size(0x2025c6) system volume information/ 12345/ 0 file(s), 2 dir(s) 共用两个目录,0个文件----进过确认正确的查看子目录下的文件: TINY4412 # fatls mmc 0 /12345 Partition1: Start Address(0x71c53a), Size(0x2025c6) ./ ../ 5567/ 0 file(s), 3 dir(s)2.2 从一个MMC文件系统(fat)中加载一个二进制文件到DDR查看帮助: TINY4412 # help fatload fatload - fatload - load binary file from a dos filesystem Usage: fatload <interface> <dev[:part]> <addr> <filename> [bytes] - load binary file 'filename' from 'dev' on 'interface' to address 'addr' from dos filesystem参数说明: <interface>: mmc 或 usb; dev: 设备编号(可以通过启动时查看或者列出存储器); part: 设备分区号; <addr>: DDR 内存地址 <filename>: 要加载二进制文件( 包含完整路径) [bytes]:要加载数据大小,字节为单位。可选的,可以不写, 不写时候默认等于文件大小。 加载文件需要SD或者EMMC有完好的文件系统。先将SD卡从开发板取出(开发板不要断电),通过读卡器插入 PC,复制一些文件到卡里,然后再重新插入开发板中 (SD卡拔出来时开发板不要断电,目的想测试一下 mmc rescan 命令作用)。SD卡拔掉之后,UBOOT一样可以运行,因为程序已经拷贝到DDR中运行了,只要不断电U-BOOT就可以正常运行。文件拷贝完将SD卡再放回开发板,先不要重新扫描 mmc 设备,直接输入 fatls 就会出错: TINY4412 # fatls mmc 0 /* 打印错误信息,因为开发板没有断电,设备0是SD卡*/ count: 1 # Tx: Inverter delay / Rx: Inverter delay count: 2 ## Tx: Basic delay / Rx: Inverter delay count: 3 ## Tx: Inverter delay / Rx: Basic delay count: 4 ### Tx: Basic delay / Rx: Basic delay count: 5 # Tx: Disable / Rx: Basic delay count: 6 ## Tx: Disable / Rx: Inverter delay count: 7 ### Tx: Basic delay / Rx: Disable count: 8 ### Tx: Inverter delay / Rx: Disable mmc read failed ERROR: -19 data.dest: 0xc3cfbbdc data.blocks: 1 data.blocksize: 512 MMC_DATA_READ ** Can't read from device 0 ** ** Unable to use mmc 0:1 for fatls ** TINY4412 # 扫描设备0,再读出信息: TINY4412 # mmc rescan 0 扫描设备 TINY4412 # fatls mmc 0 列出设备的文件目录 /* 成功列出了SD卡文件目录信息*/ Partition1: Start Address(0xa203d2), Size(0x2037b2) system volume information/ 4783928 zimage 277108 u-boot.bin 127245 纇/u-boot.pdf 5268 2015-12-30txt 731729 shell, - a.pdf 5 file(s), 1 dir(s)开始测试 fatload 命令: TINY4412 # fatload mmc 0 48000000 zimage 将 zimage文件加载到DDR的48000000地址处 Partition1: Start Address(0xa203d2), Size(0x2037b2) reading zimage 4783928 bytes read 成功加载文件的大小(字节单位) TINY4412 # md.b 48000000 打印出DDR 48000000地址处的数据 48000000: 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 ................ 48000010: 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 ................ 48000020: 02 00 00 ea 18 28 6f 01 00 00 00 00 38 ff 48 00 .....(o.....8.H. 48000030: 01 70 a0 e1 02 80 a0 e1 00 20 0f e1 03 00 12 e3 .p....... ......2.3 cmp --比较内存数据是否相同 查看帮助 TINY4412 # ? cmp cmp - memory compare 内存比较 Usage: cmp [.b, .w, .l] addr1 addr2 count 格式 注意:count 是用十六进制表示 cmp .b :以1个字节方式 cmp .w :以2 个字节方式 cmp .l :以4 个字节方式 格式: Cmp.b 地址1 地址2 比较数据的数量 ① 比较DRR两个地址数据是否相等 TINY4412 # cmp.b 48000000 49000000 10 比较两个地址数据---数量是10个 byte at 0x48000000 (0x00) != byte at 0x49000000 (0xff) Total of 0 bytes were the same 共有0字节是相同的 TINY4412 # ② 从MMC读取1个扇区的数据到DDR的两个地址 TINY4412 # mmc read 0 48000000 1 1 MMC read: dev # 0, block # 1, count 1 ... 1 blocks read: OK TINY4412 # mmc read 0 49000000 1 1 MMC read: dev # 0, block # 1, count 1 ... 1 blocks read: OK ③ 再次比较两个地址的数据 TINY4412 # cmp.b 48000000 49000000 10 ( 注意:这里的10是十六进制的10 ,转成十进制就是16) Total of 16 bytes were the same 共有16个字节都是一样的。2.4 mm --地址以自动增加的方式修改内存数据 查看帮助: TINY4412 # ? mm mm - memory modify (auto-incrementing address) 修改内存(增加的地址) Usage: mm [.b, .w, .l] address 格式: address要修改的地址 ① 先将DDR某处数据打印出来,方便修改完比较 TINY4412 # md.b 48000000 10 48000000: a3 69 d3 18 e9 7d b9 66 d1 6b d5 6e d4 79 a6 79 .i...}.f.k.n.y.y ② 修改数据 TINY4412 # mm.b 48000000 48000000: a3 ? 5 //把a3 修改为5 48000001: 69 ? 6 //把69 修改为6 48000002: d3 ? 7 48000003: 18 ? 8 48000004: e9 ? 不想修改直接按下<回车键>跳过 48000005: 7d ? 9 48000006: b9 ? TINY4412 # <INTERRUPT> 修改完直接按ctrl+c 结束 ③ 再次查看数据 TINY4412 # md.b 48000000 10 48000000: 05 06 07 08 e9 09 b9 66 d1 6b d5 6e d4 79 a6 79 .......f.k.n.y.y 修改之后的数据 将修改之前的数据与修改之后的比较,发现已经修改成功! 其他类似命令: mm.w:一次修改 2 字节 mm.l:一次修改 4 字节2.5 cp –内存拷贝 查看帮助: TINY4412 # ? cp cp - memory copy 内存复制 Usage: 用法格式 cp [.b, .w, .l] source target count 注意这里的数量是用16进制表示的 格式:cp.b 源地址 目标地址 数量 ① 读出DDR两个地址的数据,方便后面比较 TINY4412 # md.b 45000000 10 显示数据 45000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ TINY4412 # md.b 49000000 10 显示数据 49000000: a3 69 d3 18 e9 7d b9 66 d1 6b d5 6e d4 79 a6 79 .i...}.f.k.n.y.y ② 将DDR的4900000地址前10个字节拷贝到45000000地址处 TINY4412 # cp 49000000 45000000 10 ③ 将两处地址的数据再显示出来 TINY4412 # md.b 45000000 10 45000000: a3 69 d3 18 e9 7d b9 66 d1 6b d5 6e d4 79 a6 79 .i...}.f.k.n.y.y TINY4412 # md.b 49000000 10 49000000: a3 69 d3 18 e9 7d b9 66 d1 6b d5 6e d4 79 a6 79 .i...}.f.k.n.y.y 拷贝之后,将两处地址数据再次比较,两边数据是一样的。2.6 loady - 使用串口下载二进制数据到内存中U-BOOT支持的串口传输模式: loadb - load binary file over serial line (kermit mode) loads - load S-Record file over serial line loady - load binary file over serial line (ymodem mode)串口下载文件到DDR,上面是U-BOOT支持串口的3种传输模式。CRT串口终端支持的协议: 查看帮助: TINY4412 # ? loady Unknown command '' - try 'help' without arguments for list of all known commands loady - load binary file over serial line (ymodem mode) 用在串行线加载二进制文件(ymodem模式) Usage: loady [ off ] [ baud ] - load binary file over serial line with offset 'off' and baudrate 'baud' 参数说明: [ off ]: DDR 内存地址, 可选。 [ baud ]:使用多快的波特率下载, 可选,不填就表示默认的115200波特率。 示例: loady 0x40000000 115200测试loady命令:(1)下载文件到内存 TINY4412 # loady 40000000 下载文件到DDR 40000000地址 ## Ready for binary (ymodem) download to 0x40000000 at 0 bps...(2)对比数据内容(3)执行代码上面下载的bin文件是一个按键的裸机程序,可以用go命令跳转到指定地址去执行下载的代码。 TINY4412 # go 40000000 ## Starting application at 0x40000000 ... 跳转过去之后,按下按键测试! 测试结果正常,按键程序可以正常执行。2.7 go–CPU 跳转到指定的地址执行代码一旦 go 指令执行后, CPU 就会去执行指定地址处的代码。查看帮助: TINY4412 # ? go go - start application at address 'addr' 在addr处启动应用程序 Usage: go addr [arg ...] - start application at address 'addr' passing 'arg' as arguments 作为参数传递的参数测试go命令 将SD卡第一个扇区数据读到DDR内存中等待执行。读8个扇区 TINY4412 # mmc read 0 45000000 1 8 MMC read: dev # 0, block # 1, count 8 ... 8 blocks read: OK 跳转到45000000地址去执行程序 TINY4412 # go 45000000 ## Starting application at 0x45000000 ... 开始执行地址处的代码,因为扇区1开始存放的是BL1代码,重新执行启动了UBOOT OK U-Boot 2010.12 (Jan 01 2016 - 02:37:55) for TINY4412 CPU: S5PC220 [Samsung SOC on SMP Platform Base on ARM CortexA9] APLL = 1400MHz, MPLL = 800MHz Board: TINY4412 DRAM: 1023 MiB ...........................................................................................................2.8 打开关闭emmc设备引导分区 查看帮助: TINY4412 # ? emmc emmc - Open/Close eMMC boot Partition 打开/关闭emmc引导分区 Usage: emmc open <device num> emmc close <device num> 对设备读写操作,需要先打开,读写完毕,再关闭。 示例: emmc close 1 打开设备1 emmc open 1 关闭设备1 emmc close 0 打开设备0 emmc open 0 关闭设备02.9 movi 子系统----从MMC向DDR读写数据该指令在产品发布时需要用到,用来固化内核和UBOOT。查看帮助: TINY4412 # ? movi movi - movi - sd/mmc r/w sub system for SMDK board Usage: movi init - Initialize moviNAND and show card info movi read zero {fwbl1 | u-boot} {device_number} {addr} - Read data from sd/mmc 读取数据从sd / mmc movi write zero {fwbl1 | u-boot} {device_number} {addr} - Read data from sd/mmc 读取数据从sd / mmc movi read {u-boot | kernel} {device_number} {addr} - Read data from sd/mmc 读取数据从sd / mmc movi write {fwbl1 | u-boot | kernel} {device_number} {addr} - Write data to sd/mmc 写入数据到sd / mmc movi read rootfs {device_number} {addr} [bytes(hex)] - Read rootfs data from sd/mmc by size 从sd/mmc读取rootfs数据大小 movi write rootfs {device_number} {addr} [bytes(hex)] - Write rootfs data to sd/mmc by size 写rootfs sd/mmc的数据大小 movi read {sector#} {device_number} {bytes(hex)} {addr} - instead of this, you can use "mmc read" movi write {sector#} {device_number} {bytes(hex)} {addr} - instead of this, you can use "mmc write"(1)把 sd 卡中 u-boot 的第一阶段的 bl1 数据复制到内存,然后再写入 emmc 对应位置 movi read fwbl1 0 40000000; //从SD(设备编号为)拷贝bl1到DDR内存地址 emmc open 1; //打开EMMC设备 movi write zero fwbl1 1 40000000; //将DDR地址处数据写入到EMMC对应位置 emmc close 1; //关闭EMMC设备用法示例: TINY4412 # movi read fwbl1 0 40000000; reading FWBL1 ..device 0 Start 1, Count 16 MMC read: dev # 0, block # 1, count 16 ... 16 blocks read: OK 从SD卡第1个扇区开始读,连续读16个扇区 completed TINY4412 # emmc open 1; eMMC OPEN Success.!! !!!Notice!!! !You must close eMMC boot Partition after all image writing! !eMMC boot partition has continuity at image writing time.! !So, Do not close boot partition, Before, all images is written.! TINY4412 # movi write zero fwbl1 1 40000000; writing FWBL1 ..device 1 Start 0, Count 16 MMC write: dev # 1, block # 0, count 16 ... 16 blocks written: OK 从EMMC第0个扇区写,连续写16个扇区 completed TINY4412 # emmc close 1; eMMC CLOSE Success.!! 因为SD卡的特性,第0个扇区不能使用,数据只能从第1个扇区开始存放。 EMMC可以直接从第0个扇区存放数据。 所以-----EMMC的第0个扇区相当于SD卡的第1个扇区(2)把sd卡中u-boot的BL2 数据复制到内存,然后再写入 emmc 对应位置 movi read bl2 0 40000000; //从SD(设备编号为0)拷贝bl2到DDR内存地址 emmc open 1; //打开EMMC设备 movi write zero bl2 1 40000000; //将DDR地址处数据写入到EMMC对应位置 emmc close 1; //关闭EMMC设备示例: TINY4412 # movi read bl2 0 40000000 reading BL2 ..device 0 Start 17, Count 32 MMC read: dev # 0, block # 17, count 32 ... 32 blocks read: OK //从SD卡的第17个扇区开始读,连续读32个扇区。 //查看UBOOT烧写脚本可知,BL2是从SD卡第17扇区开始烧写的 completed TINY4412 # emmc open 1 eMMC OPEN Success.!! !!!Notice!!! !You must close eMMC boot Partition after all image writing! !eMMC boot partition has continuity at image writing time.! !So, Do not close boot partition, Before, all images is written.! TINY4412 # movi write zero bl2 1 40000000 writing BL2 ..device 1 Start 16, Count 32 MMC write: dev # 1, block # 16, count 32 ... 32 blocks written: OK //向EMMC的第17个扇区开始写,连续写32个扇区。 completed TINY4412 # emmc close 1 eMMC CLOSE Success.!!(3)把 sd 卡中 u-boot 复制到内存,然后再写入 emmc 对应位置 movi read u-boot 0 40000000; 将SD卡的U-BOOT.Bin读到DDR内存空间 emmc open 1; 打开EMMC设备 movi write zero u-boot 1 40000000; 将DDR的数据写入EMMC设备 emmc close 1; 关闭EMMC示例: TINY4412 # movi read u-boot 0 40000000 reading bootloader..device 0 Start 49, Count 656 MMC read: dev # 0, block # 49, count 656 ... 656 blocks read: OK 从SD卡第49个扇区开始,读取656个扇区到DDR内存 completed TINY4412 # emmc open 1 eMMC OPEN Success.!! !!!Notice!!! !You must close eMMC boot Partition after all image writing! !eMMC boot partition has continuity at image writing time.! !So, Do not close boot partition, Before, all images is written.! TINY4412 # movi write zero u-boot 1 40000000 writing bootloader..device 1 Start 48, Count 656 MMC write: dev # 1, block # 48, count 656 ... 656 blocks written: OK 向EMMC的第49个扇区,连续写入656个扇区到DDR内存 completed TINY4412 # emmc close 1 eMMC CLOSE Success.!!(4)把 sd 卡中 u-boot 安全加密数据复制到内存,然后再写入 emmc 对应位置 movi read tzsw 0 40000000; 将安全加密数据拷贝到DDR emmc open 1; 打开EMMC设备 movi write zero tzsw 1 40000000; 将DDR数据写入EMMC emmc close 1; 关闭EMMC设备示例: TINY4412 # movi read tzsw 0 40000000 reading 0 TrustZone S/W.. Start 705, Count 320 MMC read: dev # 0, block # 705, count 320 ... 320 blocks read: OK 从SD卡的第705个扇区开始,连续读取320个扇区到DDR Completed 安全加密数据是从SD的705个扇区存放的 TINY4412 # emmc open 1 eMMC OPEN Success.!! !!!Notice!!! !You must close eMMC boot Partition after all image writing! !eMMC boot partition has continuity at image writing time.! !So, Do not close boot partition, Before, all images is written.! TINY4412 # movi write zero tzsw 1 40000000; writing 1 TrustZone S/W.. Start 704, Count 320 MMC write: dev # 1, block # 704, count 320 ... 320 blocks written: OK 写入EMMC completed TINY4412 # emmc close 1 eMMC CLOSE Success.!!(5)把 sd 卡中内核数据复制到内存,然后再写入 emmc 对应位置 movi read kernel 0 40000000; 将SD卡的内核数据读到DDR内存中 movi write kernel 1 40000000; 将DDR的数据写入EMMC中示例: TINY4412 # movi read kernel 0 40000000 reading kernel..device 0 Start 1057, Count 12288 MMC read: dev # 0, block # 1057, count 12288 ... 12288 blocks read: OK 从SD卡1057扇区开始,连续读12288个扇区到DDR completed TINY4412 # movi write kernel 1 40000000 writing kernel..device 1 Start 1057, Count 12288 MMC write: dev # 1, block # 1057, count 12288 ... 12288 blocks written: OK将DDR的数据写入EMMC,从1057开始写,连续写12288个扇区 completed2.10 movi 精简命令-一键拷贝将UBOOT和内核数据固化到EMMC精简命令: movi r f 0 40008000;emmc open 1;movi w z f 1 40008000;emmc close 1; movi r b 0 40008000;emmc open 1;movi w z b 1 40008000;emmc close 1; movi r u 0 40008000;emmc open 1;movi w z u 1 40008000;emmc close 1; movi r t 0 40008000;emmc open 1;movi w z t 1 40008000;emmc close 1; movi r k 0 40008000;movi w k 1 40008000;2.11 bootcmd命令的使用bootcmd命令是设置U-BOOT启动成功后执行的命令代码。示例: set bootcmd 'mmc read 0 40000000 421 1;md.b 40000000' 格式:setenv ‘ 需要执行的命令’ Save //保存设置2.12 执行二进制文件-->bootm命令bootm命令是用来引导经过U-Boot的工具mkimage打包后的kernel image的。查看帮助: TINY4412 # ? bootm bootm - boot application image from memory //bootm从内存中启动应用程序 Usage: bootm [addr [arg ...]] - boot application image stored in memory passing arguments 'arg ...'; when booting a Linux kernel, 'arg' can be the address of an initrd image //传递参数的参数…”;当引导Linux内核,“参数”可以是映像文件的地址 Sub-commands to do part of the bootm sequence. The sub-commands must be issued in the order below (it's ok to not issue all sub-commands): start [addr [arg ...]] loados - load OS image 加载操作系统映像 cmdline - OS specific command line processing/setup 操作系统特定的命令行处理/设置 bdt - OS specific bd_t processing 操作系统特定bd_t处理 prep - OS specific prep before relocation or go go - start OS 启动操作系统示例:(1)直接引导内核 TINY4412 # mmc read 0 40007fc0 421 3000 将SD卡内核读到DDR内存空间----内核映像是从SD卡1057扇区开始存放的,连续占用了12288个扇区 (注意: 421是0x421 3000是0x3000) MMC read: dev # 0, block # 1057, count 12288 ... 12288 blocks read: OK TINY4412 # bootm 40007fc0 执行DDR--40007fc0地址处的二进制文件 Boot with zImage Starting kernel ... Uncompressing Linux... done, booting the kernel. (2)设置UBOOT启动成功自动引导内核 TINY4412 # setenv bootcmd 'mmc read 0 40007fc0 421 3000;bootm 40007fc0' U-BOOT启动成功之后自动执行 TINY4412 # save 保存设置 或者使用bootcmd=movi read kernel 0 40008000;movi read rootfs 0 41000000 100000;bootm 40008000 410000002.13 分区命令-fdisk查看帮助: TINY4412 # ? fdisk fdisk - fdisk for sd/mmc. //硬盘分区工具 Usage: fdisk -p <device_num> - print partition information //打印分区信息 fdisk -c <device_num> [<sys. part size(MB)> <user data part size> <cache part size>] - create partition //创建分区(分区单位是M)(1)查看分区信息示例 TINY4412 # fdisk -p 0 //查看SD卡分区信息 分区 大小 扇区开始地址 扇区数量(512字节一个扇区) 分区ID名称 partion # size(MB) block start # block count partition_Id 1 1028 7456058 2106822 0x06 4 0 28049408 441 0x00 (2)给SD卡分区示例(分区时--会自行在SD卡开头大约66M后的空间开始分区,因为开头部分需要用来存放内核与U-BOOT) TINY4412 # fdisk -c 0 320 2057 520 //给SD卡分区, -c 表示分区 fdisk is completed //提示分区完成 分区 大小 扇区开始地址 扇区数量(512字节一个扇区) 分区ID名称 partion # size(MB) block start # block count partition_Id 1 4416 6090216 9045762 0x0C 2 320 134343 656788 0x83 3 2062 791131 4224341 0x83 4 524 5015472 1074744 0x83 2.14 指定EMMC的分区文件系统格式U-BOOT支持格式化的文件系统格式: fatformat- fatformat - disk format by FAT32 ext3format- ext3format - disk format by ext3 ext2format- ext2format - disk format by ext2查看 fatformat命令使用帮助: TINY4412 # ? fatformat fatformat - fatformat - disk format by FAT32 指定磁盘的格式位FAT32 Usage: fatformat <interface(only support mmc)> <dev:partition num> 用法格式 - format by FAT32 on 'interface' 其他两个命令,用法一样!(1)指定分区命令-用法示例 fatformat mmc 0:1 //表示将第0个盘的第一个分区初始化为 fat ext3format mmc 0:2 //表示将第0个盘的第二个分区初始化为 ext3 ext2format mmc 0:3 //表示将第0个盘的第三个分区初始化为 ext2 ext3format mmc 0:4 //表示将第0个盘的第四个分区初始化为 ext3(2)SD卡分区文件系统格式化完毕,将SD卡插入电脑,查看SD卡的分区信息(3)将SD卡挂载进虚拟机,查看设备节点。
-
1. 前言U-boot是一种开源bootloader, 作用是用来引导操作,以及给开发人员提供测试调试工具。本身算是个精简的Linux系统,主要是负责硬件的初始化和引导,本身带有一些工具,作为引导程序,常作为嵌入式设备的引导。当真正的系统开始运行的时候U-boot就把管理权限交了出去。选择U-Boot的理由: ① 开放源码; ② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android; ③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS; ④ 较高的可靠性和稳定性; ⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等; ⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等; ⑦ 较为丰富的开发调试文档与强大的网络技术支持;U-BOOT工作模式U-Boot的工作模式有启动加载模式和下载模式。启动加载模式是Bootloader的正常工作模式,嵌入式产品发布时,Bootloader必须工作在这种模式下,Bootloader将嵌入式操作系统从FLASH中加载到SDRAM中运行,整个过程是自动的。下载模式就是Bootloader通过某些通信手段将内核映像或根文件系统映像等从PC机中下载到目标板的FLASH中。用户可以利用Bootloader提供的一些命令接口来完成自己想要的操作。当前测试uboot命令的开发板是友善之臂tin441,使用三星的EXYNOS4412,本篇文章重点是介绍uboot命令行的命令功能,用法,使用的uboot版本是2010.12,是友善之臂官方提供的uboot。2. UBOOT命令介绍2.1 帮助命令--help查看当前的UBOOT支持那些命令。 TINY4412 # help ? - alias for 'help' base - 打印一组地址偏移量 bdinfo - 开发板的信息结构 boot - boot default, i.e., run 'bootcmd' bootd - boot default, i.e., run 'bootcmd' bootelf - Boot from an ELF image in memory bootm - 从内存启动应用程序 bootp - 通过使用BOOTP / TFTP协议的网络引导映像 bootvx - Boot vxWorks from an ELF image chpart - 更改活动分区 cmp - memory compare coninfo - print console devices and information cp - 内存拷贝 crc32 - 检验和的计算 dcache - 启用或禁用数据缓存 dnw - dnw - USB设备进行初始化并准备好接受Windows server(特定的) echo - echo args to console editenv - 修改环境变量 emmc - 打开/关闭eMMC引导分区 env - 环境处理命令 exit - 退出脚本 ext2format- ext2 ext2format——磁盘格式 ext2load- 从Ext2文件系统加载二进制文件 ext2ls - 在一个目录列表文件(默认/) ext3format- ext3 ext3format——磁盘格式 false - 什么也不做,但没有成功 fastboot- fastboot——使用USB fastboot协议 fatformat- FAT32 fatformat——磁盘格式 fatinfo - fatinfo——打印文件系统的信息 fatload - fatload——从dos加载二进制文件的文件系统 fatls - 一个目录列表文件(默认/) fdisk - fdisk for sd/mmc. go - 在“addr”启动应用程序 help - 打印命令描述/使用帮助 icache - enable or disable instruction cache iminfo - print header information for application image imxtract- extract a part of a multi-image itest - return true/false on integer compare loadb - load binary file over serial line (kermit mode) loads - load S-Record file over serial line loady - load binary file over serial line (ymodem mode) loop - infinite loop on address range md - memory display mm - memory modify (auto-incrementing address) mmc - MMC子系统 mmcinfo - mmcinfo <dev num>-- display MMC info movi - movi - sd/mmc r/w sub system for SMDK board mtdparts- define flash/nand partitions mtest - simple RAM read/write test mw - memory write (fill) nfs - boot image via network using NFS protocol nm - memory modify (constant address) ping - send ICMP ECHO_REQUEST to network host printenv- print environment variables reginfo - print register information reset - Perform RESET of the CPU run - run commands in an environment variable saveenv - save environment variables to persistent storage setenv - set environment variables showvar - print local hushshell variables sleep - delay execution for some time source - run script from memory test - minimal test like /bin/sh tftpboot- boot image via network using TFTP protocol true - do nothing, successfully usb - USB sub-system version - print monitor version2.2 查看具体命令的使用方法--help格式: help <你想要查的指令> 或者 ? <你想要查的指令> , 甚至 h <你想要查的指令缩写>。 TINY4412 # help sleep sleep - 延迟执行一段时间 Usage: sleep N - 延迟执行N秒(N是_decimal_ ! ! !)2.3 打印环境变量--printenv TINY4412 # printenv baudrate=115200 bootargs=root=/dev/nfs nfsroot=192.168.18.3:/work/rootfs ip=192.168.18.123:192.168.18.3:192.168.18.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200 bootcmd=movi read kernel 0 40008000;movi read rootfs 0 41000000 400000;bootm 40008000 41000000 bootdelay=3 ethaddr=00:40:5c:26:0a:5b gatewayip=192.168.0.1 ipaddr=192.168.0.20 netmask=255.255.255.0 serverip=192.168.0.10 Environment size: 416/16380 bytesbaudrate: 当前的波特率。 一般不修改。 bootcmd: 启动命令。 bootdelay:启动命令 bootcmd 延时执行的时间。 ethaddr: 网卡 MAC 地址。 gatewayip:网关 IP 地址。 ipaddr: 开发板 IP 地址。 netmask: 子网掩码。 serverip: 服务器 IP(一般是 PC 的 IP,给开发板提供各种网络服务的主机的 IP) bootargs: u-boot 传递给操作系统内核的启动参数。(很重要)使用示例 打印指定的环境变量格式: printenv 打印的环境变量名称 TINY4412 # printenv bootargs bootargs=root=/dev/nfs nfsroot=192.168.18.3:/work/rootfs ip=192.168.18.123:192.168.18.3:192.168.18.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,1152002.4 设置环境变量--setenv 查看帮助: TINY4412 # help setenv setenv - set environment variables -->作用是设置环境变量 Usage: //命令使用方法 setenv name value ... //使用格式 - set environment variable 'name' to 'value ...' //设置环境变量“名称”“数值……格式 setenv name - delete environment variable 'name' //删除环境变量使用示例 设置上电的延时时间: TINY4412 # setenv bootdelay 10 //设置上电延时时间为10秒 TINY4412 # saveenv //保存设置 Saving Environment to SMDK bootable device... done 设置波特率示例: TINY4412 # setenv baudrate 115200 //设置波特率为115200 ## Switch baudrate to 115200 bps and press ENTER ... //设置完需要重启开发板,自动生效 删除环境变量示例: setenv baudrate //删除baudrate环境变量引用环境变量示例: TINY4412 # setenv timer 10 //随便设置一个环境变量 TINY4412 # setenv bootdelay ${timer} //引用环境变量 TINY4412 # save //保存环境变量 Saving Environment to SMDK bootable device... done TINY4412 # print baudrate=115200 bootargs=root=/dev/nfs nfsroot=192.168.18.3:/work/rootfs ip=192.168.18.123:192.168.18.3:192.168.18.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200 bootcmd=movi read kernel 0 40007fc0;bootm 40007fc0 bootdelay=10 //设置成功 ethaddr=00:40:5c:26:0a:5b gatewayip=192.168.18.1 ipaddr=192.168.18.123 netmask=255.255.255.0 serverip=192.168.18.124 timer=10 //设置的新环境变量 Environment size: 389/16380 bytes TINY4412 # 2.5 设置bootargs参数bootargs是环境变量中的重中之重,甚至可以说整个环境变量都是围绕着bootargs来设置的。coherent_pool参数: 设置DMA的大小 示例: coherent_pool=2M本地挂载示例 set bootargs root=/dev/mmcblk0p2 rootfstype=ext3 init=/linuxrc console=ttySAC0,115200 set bootargs root=/dev/mmcblk0p2 rw rootfstype=ext3 init=/linuxrc console=ttySAC0,115200NFS网络挂载示例: set bootargs root=/dev/nfs nfsroot=192.168.18.3:/work/nfs_root ip=192.168.18.123:192.168.18.3:192.168.18.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200root参数用来指定根文件系统挂载的位置。nfsroot参数是NFS网络文件系统挂载才需要设置,后面跟着服务器的NFS地址,挂载目录ip参数是设置开发板的网卡IP地址,NFS网络挂载时必须设置。init 是指定挂载文件系统之后运行的脚本,用来做一些系统初始化。2.6 查看开发板的配置信息--bdinfo TINY4412 # bdinfo arch_number = 0x00001200 ->开发板的机器码, 用来引导操作系统的内核 boot_params = 0x40000100 ->启动参数存储的内存位置 DRAM bank = 0x00000000 -> DRAM 编号,这里表示是第 0 个 DDR -> start = 0x40000000 -->DRAM 的起始地址 -> size = 0x10000000 -->DRAM 的大小 ( 0x10000000 /1024 /1024 = 256M) DRAM bank = 0x00000001 -> DRAM 编号,这里表示是第 1 个 DDR -> start = 0x50000000 -->DRAM 的起始地址 -> size = 0x10000000 -->DRAM 的大小( 0x10000000 /1024 /1024 = 256M) DRAM bank = 0x00000002-> DRAM 编号,这里表示是第 2 个 DDR -> start = 0x60000000 -->DRAM 的起始地址 -> size = 0x10000000 ->DRAM 的大小( 0x10000000 /1024 /1024 = 256M) DRAM bank = 0x00000003-> DRAM 编号,这里表示是第 3 个 DDR -> start = 0x70000000 ->DRAM 的起始地址 -> size = 0x0FF00000 ->DRAM 的大小( 0x10000000 /1024 /1024 = 256M) ethaddr = 00:40:5c:26:0a:5b ->网卡 MAC 地址(DM9600) ip_addr = 192.168.0.20 ->开发板的 IP baudrate = 0 bps ->波特率,这里是代码有问题,应该 115200 TLB addr = 0x3FFF0000 ->MMU(CPU) 映射表存储位置 relocaddr = 0xC3E00000 ->代码重新定位的地址 reloc off = 0x00000000 ->重定位地址 irq_sp = 0xC3CFBF58 ->irq堆栈指针 sp start = 0xC3CFBF50 ->开始地址堆栈指针 FB base = 0x00000000 ->framebuffer基地址2.7 内存数据显示->md查看帮助: TINY4412 # ? md md - memory display 内存数据显示---只能显示内存中的数据,就是说只能在DDR地址中操作 Usage: md [.b, .w, .l] address [# of objects] Md.b : 以字节方式显示数据 Md.w : 以字(2 个字节) Md.l : 以双字(4 个字节) 以上表示以字节、字(2 个字节)、双字(4 个字节)为单位进行显示格式: Md.b <要显示的地址> [显示的数据个数] TINY4412 # md.b 1000000 10 //将起始地址1000000处的10个数据显示到终端 01000000: 06 00 00 ea fe ff ff ea fe ff ff ea fe ff ff ea ................示例: TINY4412 # md.b 1000000 10 一个字节显示: 01000000: 06 00 00 ea fe ff ff ea fe ff ff ea fe ff ff ea ................ TINY4412 # md.w 1000000 10 两个字节显示 01000000: 0006 ea00 fffe eaff fffe eaff fffe eaff ................ 01000010: fffe eaff fffe eaff 301a ea00 301b ea00 .........0...0.. TINY4412 # md.l 1000000 10 四个字节显示 01000000: ea000006 eafffffe eafffffe eafffffe ................ 01000010: eafffffe eafffffe ea00301a ea00301b .........0...0.. 01000020: e59f01a4 e3a01000 e5801000 e59f019c ................ 01000030: e5900000 e200003e e330003e 1a00000d ....>...>.0.....2.8 复制内存命令 cp查看帮助: TINY4412 # help cp cp - memory copy 内存拷贝 --只能在内存中拷贝,就是说只能在DDR地址中操作 Usage: cp [.b, .w, .l] source target count 源地址 目标地址 数量个数示例1: TINY4412 # cp 100000 4000000 10 从起始地址100000开始拷贝10个数据到4000000的地址处示例2: TINY4412 # md.b 46000000 46000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ TINY4412 # md.b 10000000 10000000: 11 20 41 e4 08 00 05 08 05 00 00 00 10 00 00 00 . A............. TINY4412 # cp 10000000 46000000 10 从起始地址10000000开始拷贝10个数据到46000000的地址处 TINY4412 # md.b 46000000 46000000: 11 20 41 e4 08 00 05 08 05 00 00 00 10 00 00 00 . A.............2.9 查看EMMC的信息->mmcinfo查看帮助: TINY4412 # help mmcinfo mmcinfo - mmcinfo <dev num>-- display MMC info >输出指定编号 mmc 的信息, <dev num>是要指定的编号 Usage: mmcinfo 编号说明: mmc 的编号是会变化的, Tiny4412 板上有 EMMC,有SD卡。这两个都归类为 MMC。 编号是0,1。 但是谁是0,谁是 1,是不确定的, 和启动方式有关。 在哪个存储器启动,哪个就是编号就是0。查看SD卡信息: TINY4412 # mmcinfo 0 Device: S3C_HSMMC2 设备名称 Manufacturer ID: 2 制造商标识 ID OEM: 544d 设备制造商 Name: SE08G 名称 Tran Speed: 0 Rd Block Len: 512 每一块的大小,字节为单位 SD version 2.0 SD卡的版本 High Capacity: Yes 是否是大容量卡 Size: 7460MB (block: 15278080) 容量大小,(总共有多少个块) Bus Width: 4-bit 总线宽度,SD卡接口是4条线 Boot Partition Size: 0 KB 引导分区大小查看EMMC的信息: TINY4412 # mmcinfo 1 Device: S5P_MSHC4 设备 Manufacturer ID: 15 制造商ID OEM: 100 原始设备制造商 Name: 4YMD3 名称 Tran Speed: 0 Tran速度 Rd Block Len: 512 每一块的大小,字节为单位 MMC version 4.0 MMC版本 High Capacity: Yes 是否是大容量卡 Size: 3728MB (block: 7634944) 卡的容量和总共的块大小 Bus Width: 8-bit 总线宽度 Boot Partition Size: 4096 KB 引导分区大小2.10 mmc命令子系统mmc不是单独的命令,他是一个子系统,支持多个命令。查看mmc子系统的帮助信息 TINY4412 # help mmc mmc - MMC sub system MMC子系统 Usage: mmc read <device num> addr blk# cnt --从 mmc 指定扇区读取数据到 ddr 中 mmc write <device num> addr blk# cnt --写 ddr 中的数据到指定 mmc 扇区中 mmc rescan <device num> --重新扫描指定设备, 相当于重新初始化 mmc erase <boot | user> <device num> <start block> <block count> --擦除指定扇区 mmc list - lists available devices --列出有效的 mmc 设备参数说明: <device num>: mmc 编号,编号原则同前面说的,就是对哪一个设备操作。 addr: DDR3 内存地址; blk#: 要读/写的 mmc 扇区地址起始地址; cnt: 要读/写的 mmc 扇区数量; boot: 引用分区,一般是操作 bl1,bl2,u-boot 的 mmc 扇区范围。 user: 用户分区, 一般是操作内核,文件系统的 mmc 扇区范围。 <start block>:要擦除的 mmc 扇区起始地址; <block count>:要擦除的 mmc 扇区数量;mmc 命令中的参数都是 16 进制表示,不是 10 进制表示(1)从MMC扇区读数据到DDR内存中->mmc read 格式:mmc read <device num> addr blk# cnt blk#:要读/写的 mmc 扇区的起始地址 (十六进制表示) Cnt :要读/写的 mmc 扇区数量(十六进制表示) addr: DDR3 内存地址; TINY4412 # mmc read 0 45000000 1 1 MMC read: dev # 0, block # 1, count 1 ... 1 blocks read: OK 这里是从SD卡的第1个扇区开始,读取一个扇区的数据到DDR的45000000地址处! 示例: TINY4412 # md.b 48000000 10 48000000: ff ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ................ //从SD卡第一个扇区开始,读取一个扇区的数据到DDR的48000000地址处 TINY4412 # mmc read 0 48000000 1 1 MMC read: dev # 0, block # 1, count 1 ... 1 blocks read: OK TINY4412 # md.b 48000000 10 48000000: a3 69 d3 18 e9 7d b9 66 d1 6b d5 6e d4 79 a6 79 .i...}.f.k.n.y.y(2)mmc write --写 ddr 中的数据到指定 mmc 扇区中 格式:mmc write <device num> addr blk# cnt --将 ddr 中的数据到写到指定mmc 扇区中 blk#:要读/写的 mmc 扇区的起始地址(十六进制表示) Cnt :要读/写的 mmc 扇区数量(十六进制表示) addr:DDR3 内存地址; 示例: TINY4412 # mmc write 0 48000000 1 1 从DDR 48000000地址处,写1个扇区的数据到SD的第1个扇区 MMC write: dev # 0, block # 1, count 1 ... 1 blocks written: OK(3)擦除指定扇区 格式: mmc erase <boot | user> <device num> <start block> <block count> --擦除指定扇区 参数说明: <start block>:要擦除的 mmc 扇区起始地址 <block count>:要擦除的 mmc 扇区数量 boot: 引用分区 User: 用户分区 为了方便比较,先将SD卡的第1个扇区内容读到DDR中。 读出第10个扇区的数据 TINY4412 # mmc read 0 48000000 1 1 MMC read: dev # 0, block # 1, count 1 ... 1 blocks read: OK 显示第1个扇区的数据 TINY4412 # md.b 48000000 30 48000000: a3 69 d3 18 e9 7d b9 66 d1 6b d5 6e d4 79 a6 79 .i...}.f.k.n.y.y 48000010: 07 00 00 ea fe ff ff ea fe ff ff ea fe ff ff ea ................ 48000020: fe ff ff ea fe ff ff ea fe ff ff ea fe ff ff ea ................ 擦除SD卡的第一个扇区 TINY4412 # mmc erase user 0 1 1 START: 1 BLOCK: 1 开始1扇区,擦除1扇区 high_capacity: 1 高容量 Capacity: 15278080 容量 Erase 擦除 512 B erase Done 512字节 MMC erase Success.!! MMC擦除成功。! ! 再读再显示 TINY4412 # mmc read 0 40000000 1 1 MMC read: dev # 0, block # 1, count 1 ... 1 blocks read: OK TINY4412 # md.b 40000000 30 40000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 40000010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 40000020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 恢复数据,再读再显示 TINY4412 # mmc write 0 48000000 1 1 将DDR里的数据写入SD卡中 MMC write: dev # 0, block # 1, count 1 ... 1 blocks written: OK TINY4412 # mmc read 0 40000000 1 1 再次读出数据 MMC read: dev # 0, block # 1, count 1 ... 1 blocks read: OK TINY4412 # md.b 40000000 30 显示---数据已经恢复 40000000: a3 69 d3 18 e9 7d b9 66 d1 6b d5 6e d4 79 a6 79 .i...}.f.k.n.y.y 40000010: 07 00 00 ea fe ff ff ea fe ff ff ea fe ff ff ea ................ 40000020: fe ff ff ea fe ff ff ea fe ff ff ea fe ff ff ea ................(4)列出MMC设备--mmc list TINY4412 # mmc list S3C_HSMMC2: 0 --- 0 编号的 mmc 设备,这里接 SD 卡 S5P_MSHC4: 1 --- 1 编号的 mmc 设备,这里接 开发板板载的EMMC2.11 查看MMC分区信息--fatinfo TINY4412 # help fatinfo fatinfo - fatinfo - print information about filesystem 打印文件系统信息 Usage: fatinfo <interface> <dev[:part]> 格式说明 - print information about filesystem from 'dev' on 'interface' 参数说明: <interface>: mmc 或 usb; dev: 设备编号; part: 设备分区号 查看第0个设备信息---这里是SD卡: TINY4412 # fatinfo mmc 0 -----Partition 1----- Partition1: Start Address(0x2e2e2e2e), Size(0x2e2e2e2e) 分区1 起始地址 大小 ------------------------ -----Partition 2----- Partition1: Start Address(0x2e2e2e2e), Size(0x2e2e2e2e) ------------------------ -----Partition 3----- Partition1: Start Address(0x2e2e2e2e), Size(0x2e2e2e2e) ------------------------ -----Partition 4----- Partition1: Start Address(0x2e2e2e2e), Size(0x2e2e2e2e) ------------------------ Interface: SD/MMC 接口 Device 0: Vendor: Man 02544d Snr c9226e33 Rev: 2.1 Prod: SE08G Type: Removable Hard Disk 类型:可移动硬盘 Capacity: 14.5 MB = 0.0 GB (29840 x 512) Partition 1: Filesystem: FAT32 "NO NAME "下一篇继续介绍。
-
1. 前言如果大家做过linux系统移植、或者Linux相关开发,对根文件系统这个名词应该很熟悉,在搭建嵌入式开发环境过程中,移植bootloader,移植kernel制作根文件系统是必须要做3件事情。根文件系统是内核启动时所挂载mount的第一个文件系统,系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。根文件系统作为系统的根,系统启动之后为完成一些初始化配置,操作。比如:系统登录输入的开机密码、开机账户这些信息就是存放在根文件系统里的。根文件系统挂载的流程:先运行uboot引导内核,内核启动完成之后,根据配置的环境变量,找到根文件系统的位置,将/dev/xxx挂载,然后执行/linuxrc程序,等其执行完后。切换根目录,再挂载具体的根文件系统,根文件系统执行完之后,执行init的进程,也就第一个用户进程,对系统进行各种初始化的操作。上面说的/dev/xxx 是根文件系统存放位置的设备节点,根文件系统支持从SD卡、光盘、EMMC、NFS网络位置进行挂载。/linuxrc 是根文件系统制作之后,在顶层目录生成的文件。根文件系统必须具备的几个重要目录:/bin、/etc、/lib、/dev其中/bin目录存放系统的基本命令,比如:ls 这些命令就放在这里面。/etc存放系统的配置文件,根文件系统挂载之后会执行/etc下的配置文件完成对系统初始化。/lib目录下存放系统运行需要的共享库文件,也就是动态库. 格式是: xxx.so/dev目录下存放内核生成的设备节点,这个目录下的文件是内核自动生成的。2. 根文件系统制作制作根文件系统,需要用到Busybox工具包。BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令,也包含了 Linux 系统的自带的shell。Busybox下载地址: cid:link_0接下来对Busybox进行配置、交叉编译、安装,生成根文件系统需要的基本目录文件。我这里用到的版本是1.23.2,下载之后,解压busybox-1.23.2.tar.bz2 到Linux系统指定目录下。下面是操作的步骤: [wbyq@wbyq ~]$ mkdir ~/work/busybox -p [wbyq@wbyq ~]$ tar xvf busybox-1.23.2.tar.bz2 -C ~/work/busybox/ [wbyq@wbyq ~]$ cd work/busybox/ [wbyq@wbyq busybox]$ cd busybox-1.23.2/ [wbyq@wbyq busybox-1.23.2]$ make menuconfig Busybox Settings ---> Build Options ---> (arm-linux-) Cross Compiler prefix Installation Options ("make install" behavior) ---> (/home/wbyq/work/rootfs) BusyBox installation prefix [wbyq@wbyq busybox-1.23.2]$ make && make install安装完成之后,在配置的目录下就可以看到生成的文件了。3. 完善根文件系统上面busybox生成的文件只是根文件系统的基本文件,接下来还需要自己做一些完善,比如: 修改etc配置、拷贝动态库等等。(1). 拷贝动态库 [wbyq@wbyq lib]$ cp ~/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/sys-root/lib/* ./ -rd [wbyq@wbyq lib]$ cp ~/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/sys-root/usr/lib/* ./ -rd [wbyq@wbyq lib]$ sudo cp ~/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/lib/* ./ -rd(2).创建fstab文件,完善/etc目录 [wbyq@wbyq rootfs]$ cp /etc/fstab etc/ [wbyq@wbyq rootfs]$ cp /etc/passwd etc/ [wbyq@wbyq rootfs]$ cp /etc/group etc/(3). 创建inittab cp busybox解压目录/examples/inittab /tiny4412/rootfs/etc/ [wbyq@wbyq rootfs]$ cp ../busybox/busybox-1.23.2/examples/inittab etc/(4). 修改上面拷贝的inittab文件 ::sysinit:/etc/init.d/rcS #set初始化执行的文件 console::askfirst:-/bin/sh #需要加上console askfirst表示需要按下回车才可以进入系统 respawn表示开机直接进入系统 ::ctrlaltdel:/sbin/reboot #指定重启命令 ::shutdown:/bin/umount -a -r #指定关机时执行的命令(5). 创建 etc/init.d/rcS 文件 [wbyq@wbyq rootfs]$ touch etc/init.d/rcS [wbyq@wbyq rootfs]$ chmod 777 etc/init.d/rcS [wbyq@wbyq rootfs]$ gedit etc/init.d/rcS 写入以下代码: mount -a mkdir /dev/pts mount -t devpts devpts /dev/pts echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s /bin/hostname wbyq(6).创建 etc/profile文件 [wbyq@wbyq rootfs]$ touch etc/profile [wbyq@wbyq rootfs]$ gedit etc/profile 编写以下代码: USER="id-un" LOGNAME=$USER PS1='[\u@\h \W]$ ' PATH=$PATH HOSTNAME='/bin/hostname' export USER LOGNAME PS1 PATH HOSTNAME参数解释: PS1是命令行样式设置的环境变量。
-
1. 前言Tiny4412开发是友善之臂推出的Android、Linux学习开发板,CPU采用三星的EXYNOS4412,32位芯片,属于Cortex-A系列,主频是1.5GHZ,可以运行ubuntu、Android5.0、纯Linux等操作系统。这篇文章就介绍利用这款开发板完成裸机开发,不涉及操作系统,直接当做单片机一样,完成LED灯、蜂鸣器的编程,了解这款芯片与常规的Cortex-M系列芯片编程有何区别。核心板如图:下面是开发板的实物图:开发板的配置:2. 搭建交叉编译环境进行裸机编程之前,需要先搭建交叉编译环境,安装arm-linux-gcc交叉编译器,交叉编译后的程序才能在开发板上运行。什么叫交叉编译? PC机编译、在嵌入式开发板运行这种模式就叫交叉编译。交叉编译器在开发板的光盘里有提供,直接拷贝到PC机Linux下解压,配置环境变量接口。详细操作步骤如下: 1. 在Linux用户目录下创建一个目录: mkdir work/arm-linux-gcc -p 2. 将交叉编译器拷贝到Linux系统共享目录。再解压到arm-linux-gcc目录下。 tar xvf arm-linux-gcc-4.5.1-v6-vfp-20120301.tgz -C /home/wbyq/work/arm-linux-gcc/ 3. 添加系统环境变量 (1). root用户: 需要将代码写在/etc/profile文件中 (2). 普通用户: 需要将代码写在 用户目录下的.bash_profile文件中 profile文件系统上电的时候会自动执行。 添加环境变量的命令: export PATH=/home/wbyq/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/bin:$PATH 参数: export 导出--全局声明 PATH 系统环境变量的名称. 作用: 保存Linux系统可执行文件的搜索路径. 输出环境变量的值: [wbyq@wbyq ~]$ echo $PATH /home/wbyq/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/bin:/usr/lib/qt-3.3/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/wbyq/bin export LD_LIBRARY_PATH=/mnt/hgfs/linux-share-dir/sum:$LD_LIBRARY_PATH 参数: LD_LIBRARY_PATH 系统环境变量的名称. 作用: 保存Linux系统动态库的搜索路径. xxx.so 4. 生效环境变量 (1). 立即生效: 当前终端有效 [wbyq@wbyq ~]$ source .bash_profile (2). 退出用户、重新登录系统,实现永久生效 5. 测试交叉编译器. 学习基本用法 [wbyq@wbyq linux_2021]$ arm-linux-gcc app.c [wbyq@wbyq linux_2021]$ ls a.out app.c [wbyq@wbyq linux_2021]$ ./a.out bash: ./a.out: cannot execute binary file [wbyq@wbyq linux_2021]$ gcc app.c -o app1 [wbyq@wbyq linux_2021]$ arm-linux-gcc app.c -o app2 [wbyq@wbyq linux_2021]$ ls a.out app1 app2 app.c [wbyq@wbyq linux_2021]$ file app1 app1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped [wbyq@wbyq linux_2021]$ file app2 app2: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.27, not stripped3. 点亮LED灯如果学习过单片机(51,STM32、MSP430、AVR之类的)编程,完成当前章节的内容应该就很容易。要完成LED灯的控制,需要完成以下几个步骤: 1. 查看原理图 2. 配置GPIO口 3. 控制GPIO口输出电平控制LED 4. 编译程序: xxx.lds 链接文件 设备: 字符设备、块设备、网络设备 SD卡设备: /dev/sdb 查看块大小: cat /sys/block/sdb/size 单位是块(1块512字节) 5. 烧写到开发板测试 dd iflag=dsync oflag=dsync if=./E4412_N.bl1.bin of=/dev/sdb seek=1 参数: if=./E4412_N.bl1.bin 要写到SD卡上的文件 of=/dev/sdb SD卡设备 seek=1 跳过的块. 一个块==512字节 执行烧写命令: [wbyq@wbyq sd_fuse]$ sudo ./sd_write.sh /dev/sdb ../main.bin (1)查看原理图,查找LED的接线位置tiny4412开发板是分两层设计的,一个核心板,一个底板,LED灯是焊接在核心板上面,原理图就得打开核心板这份。(2)查看芯片手册,了解GPIO口如何配置配置方法也很好理解,在手册里做了详细介绍。LED属于输出控制器件,需要将GPIO口配置成输出模式。GPIO的模式配置由CON寄存器完成,输出控制由DAT寄存器完成。(3)编写代码 /* LED的寄存器 GPM4_0 1 2 3*/ #define GPM4CON (*(volatile unsigned int *)0x110002E0) #define GPM4DAT (*(volatile unsigned int *)0x110002E4) int main(void) { /*配置GPIO口模式--配置LED灯*/ GPM4CON&=0xFFFF0000; GPM4CON|=0x00001111; /*3. 配置GPIO口模式--配置按键*/ GPX3CON&=0xFF0000FF; GPM4DAT&=~(1<<0); GPM4DAT&=~(1<<1); GPM4DAT&=~(1<<2); GPM4DAT&=~(1<<3); while(1) { } return 0; }(4)Makefile编写 CC=arm-linux-gcc main_sp.bin:start.o main.o arm-linux-ld -Tmain.lds -o main_sp.elf $^ arm-linux-objcopy -O binary main_sp.elf main.bin arm-linux-objdump -D main_sp.elf > main_sp_elf.dis %.o : %.S $(CC) -o $@ $< -c %.o : %.c $(CC) -o $@ $< -c clean: rm *.o *.elf *.bin *.dis -f(5)代码烧写脚本 # # Copyright (C) 2011 Samsung Electronics Co., Ltd. # http://www.samsung.com/ # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. #modify by zth # #################################### if [ -z $2 ] #检查传入的第三个参数长度是否为 0 then echo "传参数顺序: ./脚本.sh <SD卡设备> <要烧录的文件>" exit 0 fi if [ -b $1 ] #检查第二个参数是否是块设备 then echo "$1 SD卡设备正常!" else echo "$1 SD卡设备错误!" exit 0 #退出脚本 fi if [ -e $2 ] #检查第三个参数(就是要烧录的文件是否存在) then echo "$2 文件存在." else echo "$2 文件不存在." exit 0 #退出脚本 fi BDEV_NAME=`basename $1` #变量赋值--块设备名称 BDEV_SIZE=`cat /sys/block/${BDEV_NAME}/size` if [ ${BDEV_SIZE} -le 0 ]; then echo "Error: NO media found in card reader." exit 1 fi if [ ${BDEV_SIZE} -gt 32000000 ]; then echo "Error: Block device size (${BDEV_SIZE}) is too large" exit 1 fi E4412_UBOOT=$2 #将要烧录文件赋值给变量E4412_UBOOT MKBL2=./mkbl2 #需要当前路径下有一个mkbl2文件 if [ ! -f ${E4412_UBOOT} ]; then #检测文件是否是普通文件,非目录和设备文件 echo "$2 文件非普通文件!请检查文件是否正确!" exit -1 #退出脚本文件 fi if [ ! -f ${MKBL2} ]; then #检测文件是否是普通文件,非目录和设备文件 echo "当前目录下缺少 mkbl2 文件!" exit -1 #退出脚本文件 fi ${MKBL2} ${E4412_UBOOT} bl2.bin 14336 #14K 通过mkbl2文件烧录程序 地址是14336 #./mkbl2 main.bin bl2.bin 14336 #################################### # fusing images signed_bl1_position=1 bl2_position=17 uboot_position=49 tzsw_position=705 #<BL1 fusing> echo "---------------------------------------" echo "BL1 fusing" #烧录命令 ./xx.sh /dev/sdb main.bin dd iflag=dsync oflag=dsync if=./E4412_N.bl1.bin of=$1 seek=$signed_bl1_position #<BL2 fusing> echo "---------------------------------------" echo "BL2 fusing" #烧录命令 dd iflag=dsync oflag=dsync if=./bl2.bin of=$1 seek=$bl2_position sync #输出信息 echo "---------------------------------------" echo "程序烧录成功!" echo "请拔出SD卡,放入开发板运行!!"
-
1. 前言Tiny4412开发是友善之臂推出的Android、Linux学习开发板,CPU采用三星的EXYNOS4412,32位芯片,属于Cortex-A系列,主频是1.5GHZ,可以运行ubuntu、Android5.0、纯Linux等操作系统。上篇文章已经介绍过LED裸机编程,完成LED灯控制,这篇文章就介绍利用这款开发板继续完成按键编程,了解GPIO口基本的输入输出配置。下面是开发板的实物图:开发板的配置:2. 按键编程要完成程序开发,需要先搭建交叉编译环境,安装arm-linux-gcc编译器,上篇文章已经介绍了,这篇就不再重复。环境搭建好,就得看原理图,了解按键接在CPU的哪个IO口,了解按键是什么电平表示按下,再去看芯片手册,找到对应IO口的配置寄存器,完成GPIO模式配置,最终完成按键检测。2.1 查看原理图tiny4412开发板有两个原理图,分别对应底板和核心板,按键是接在底板上,下面就分别打开底板、核心板原理图,找到按键具体的接线位置。从原理图里看到,按键一共有4个按键,分别接在GPX3_2 GPX3_3 GPX3_4 GPX3_5 这些GPIO口上。2.2 查看芯片手册在芯片手册里找到GPX3这个寄存器的位置。GPX3CON是模式配置寄存器。GPX3DAT是数据输出输入控制寄存器。按键是检测外部电平来判断按键是否按下松开,需要配置成输入模式。2.3 编写按键检测代码下面代码实现效果: 检测按键是否按下,按键按下后就点亮LED灯,开启蜂鸣器,按键松开就关闭LED灯,关闭蜂鸣器。 /* 蜂鸣器的寄存器---GPD0_0*/ #define GPD0CON (*(volatile unsigned int *)0x114000A0) #define GPD0DAT (*(volatile unsigned int *)0x114000A4) /* LED的寄存器 GPM4_0 1 2 3*/ #define GPM4CON (*(volatile unsigned int *)0x110002E0) #define GPM4DAT (*(volatile unsigned int *)0x110002E4) /* KEY的寄存器 GPX3 2 3 4 5*/ #define GPX3CON (*(volatile unsigned int *)0x11000C60) #define GPX3DAT (*(volatile unsigned int *)0x11000C64) //BEEP---GPD0_0 int main(void) { /*1. 配置GPIO口模式--配置蜂鸣器*/ GPD0CON&=~(0xF<<0*4);//清除寄存器配置 GPD0CON|=0x1<<0*4; //配置GPIO为输出模式 /*2. 配置GPIO口模式--配置LED灯*/ GPM4CON&=0xFFFF0000; GPM4CON|=0x00001111; /*3. 配置GPIO口模式--配置按键*/ GPX3CON&=0xFF0000FF; while(1) { if(!(GPX3DAT&1<<2)) //判断按键是否按下 { GPD0DAT|=1<<0; //输出高电平 GPM4DAT&=~(1<<0); } else if(!(GPX3DAT&1<<3)) //判断按键是否按下 { GPD0DAT|=1<<0; //输出高电平 GPM4DAT&=~(1<<1); } else if(!(GPX3DAT&1<<4)) //判断按键是否按下 { GPD0DAT|=1<<0; //输出高电平 GPM4DAT&=~(1<<2); } else if(!(GPX3DAT&1<<5)) //判断按键是否按下 { GPD0DAT|=1<<0; //输出高电平 GPM4DAT&=~(1<<3); } else { GPD0DAT&=~(1<<0); GPM4DAT|=0xF<<0; } } return 0; }2.4 烧写程序测试程序编译之后会生成main.bin文件,将SD卡插入到电脑上,运行程序烧写脚本,执行dd命令,烧写成功后。拔出SD卡,插在开发板上,选择从SD卡启动,然后按下按键测试效果。 [wbyq@wbyq sd_fuse]$ sudo ./sd_write.sh /dev/sdb ../main.bin 在三星官方的手册里写明了,如果使用SD卡,SD卡内部数据该如何存放。上面烧写程序就是安装这个说明编写的代码,使用dd命令将可执行文件烧写到SD卡的对应位置。如果使用EMMC,就是按下下面的布局:
-
【开源资料】XQTyer评估板例程使用手册链接:https://share.weiyun.com/8csewUvh 密码:8r9by7 XQ6657Z35/45-EVM(XQTyer 评估板)是一款基于 TI KeyStone 架构 C6000 系列 TMS320C6657双核C66x 定点/浮点 DSP以及 Xilinx Zynq-7000 系列 XC7Z035/045 SoC 处理器设计的高端异构多核评估板,由核心板与评估底板组成。
-
1. 前言平时工作中编写开发技术文档,或者学生在编写论文时,经常会上网搜索一些参考文献、文档。比如: 上网搜索相似的内容参考一下或者引用别人的一段文字,有时候看到一篇较好的内容想要保存等等。这个过程中会发现,很多网站的提供的页面都是不能复制粘贴的,或者直接是图片形式提供,为了方便能获取这些文字,当前就利用华为云提供的 通用文字识别接口,识别图片里的文本内容,方便复制文字。这个功能QQ上也集成了,使用很方便,这里利用华为云的接口实现一个与QQ类似的功能,截图之后识别图片里包含的文本内容。这个文字识别接口里不仅仅有通用文字识别功能,还支持很多其他功能:比如身份证、驾驶证、保险单、手写文本、火车票,行驶证.......等等功能。还支持用户自定义识别模板,指定需要识别的关键字段,实现用户特定格式图片的自动识别和结构化提取。2. 文本识别接口使用介绍2.1 开通服务地址: cid:link_1这个文字识别服务是按调用次数计费的,每个用户每月有1000次的免费调用次数,开通服务后就可以使用。2.2 接口地址官网帮助文档: cid:link_2POST https://{endpoint}/v2/{project_id}/ocr/general-text 示例: https://ocr.cn-north-4.myhuaweicloud.com/v2/0e5957be8a00f53c2fa7c0045e4d8fbf/ocr/general-text 请求头: { "X-Auth-Token": "******", "Content-Type": "application/json;charset=UTF-8" } 请求体: { "image": ----这是图片的bas64编码 } 响应结果: { "result": { "words_block_count": 13, "words_block_list": [ { "words": "撤,还是不撤?", "location": [ [ 43, 39 ], [ 161, 39 ], [ 161, 60 ], [ 43, 60 ] ] }, { "words": "让我更骄傲的是公司在大灾面前的表现。", "location": [ [ 72, 95 ], [ 332, 95 ], [ 332, 113 ], [ 72, 113 ] ] }, { "words": "2011年3月11日14时46分,日本东北部海域发生里氏9.0级", "location": [ [ 71, 122 ], [ 482, 122 ], [ 482, 142 ], [ 71, 142 ] ] }, { "words": "地震并引发海啸。那一刻,我们正在距离东京100公里的热海开会,", "location": [ [ 41, 149 ], [ 481, 149 ], [ 481, 171 ], [ 41, 171 ] ] }, { "words": "感觉“咚”", "location": [ [ 42, 180 ], [ 114, 180 ], [ 114, 199 ], [ 42, 199 ] ] }, { "words": "地被震了一下。面对地震,", "location": [ [ 115, 178 ], [ 296, 178 ], [ 296, 199 ], [ 115, 199 ] ] }, { "words": "大家都很镇定,", "location": [ [ 300, 179 ], [ 400, 179 ], [ 400, 197 ], [ 300, 197 ] ] }, { "words": "直到看到电", "location": [ [ 405, 179 ], [ 483, 179 ], [ 483, 196 ], [ 405, 196 ] ] }, { "words": "视上触目惊心的画面:15时 25 分,海啸到达陆前高田市海岸;15时", "location": [ [ 41, 206 ], [ 485, 206 ], [ 485, 228 ], [ 41, 228 ] ] }, { "words": "26分,海啸到达陆前高田市中心;15时43分,陆前高田市依稀只能", "location": [ [ 40, 234 ], [ 486, 234 ], [ 486, 258 ], [ 40, 258 ] ] }, { "words": "看到四层高的市府大楼的屋顶,一瞬间,城镇就变成了汪洋……对", "location": [ [ 40, 262 ], [ 487, 262 ], [ 487, 287 ], [ 40, 287 ] ] }, { "words": "我来说,地震跟家常便饭一样,可眼前的灾难比以往任何一次都要", "location": [ [ 40, 292 ], [ 487, 292 ], [ 487, 317 ], [ 40, 317 ] ] }, { "words": "惨烈,完全超出了我的预期。", "location": [ [ 41, 326 ], [ 231, 326 ], [ 231, 345 ], [ 41, 345 ] ] } ], "direction": -1 } }在请求参数里的X-Auth-Token参数比较重要,调用华为云的任何API接口都需要这个参数,获取方式可以看前面的文章。比如这篇文章: cid:link_32.3 在线调试接口地址: cid:link_0使用调试接口想体验识别效果,图片的数据支持base64编码、http网络图片地址传入,测试非常方便。关于获取图片base64编码的方式,在文档里也有介绍,直接通过浏览器获取。3. 实现代码代码采用QT编写的,请求API接口实现调用。其他语言方法是一样的。3.1 实现效果3.2 核心代码//解析反馈结果 void Widget::replyFinished(QNetworkReply *reply) { QString displayInfo=""; int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); //读取所有数据 QByteArray replyData = reply->readAll(); qDebug()<<"状态码:"<<statusCode; qDebug()<<"反馈的数据:"<<QString(replyData); //更新token if(function_select==3) { displayInfo="token 更新失败."; //读取HTTP响应头的数据 QList<QNetworkReply::RawHeaderPair> RawHeader=reply->rawHeaderPairs(); qDebug()<<"HTTP响应头数量:"<<RawHeader.size(); for(int i=0;i<RawHeader.size();i++) { QString first=RawHeader.at(i).first; QString second=RawHeader.at(i).second; if(first=="X-Subject-Token") { Token=second.toUtf8(); displayInfo="token 更新成功."; //保存到文件 SaveDataToFile(Token); break; } } QMessageBox::information(this,"提示",displayInfo,QMessageBox::Ok,QMessageBox::Ok); return; } //判断状态码 if(200 != statusCode) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QString error_str=""; QJsonObject obj = document.object(); QString error_code; //解析错误代码 if(obj.contains("error_code")) { error_code=obj.take("error_code").toString(); error_str+="错误代码:"; error_str+=error_code; error_str+="\n"; } if(obj.contains("error_msg")) { error_str+="错误消息:"; error_str+=obj.take("error_msg").toString(); error_str+="\n"; } //显示错误代码 QMessageBox::information(this,"提示",error_str,QMessageBox::Ok,QMessageBox::Ok); } } return; } //结果返回 if(function_select==1) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QJsonObject obj = document.object(); QString error_code; //解析 if(obj.contains("result")) { QJsonObject obj1=obj.take("result").toObject(); QString bank_name; QString card_number; QString type; QString text; if(obj1.contains("bank_name")) { bank_name=obj1.take("bank_name").toString(); } if(obj1.contains("card_number")) { card_number=obj1.take("card_number").toString(); } if(obj1.contains("type")) { type=obj1.take("type").toString(); } text="发卡行:"+bank_name+"\n"; text+="卡号:"+card_number+"\n"; text+="卡类型:"+type+"\n"; ui->plainTextEdit->setPlainText(text); } } } } //结果返回 if(function_select==2) { //解析数据 QJsonParseError json_error; QJsonDocument document = QJsonDocument::fromJson(replyData, &json_error); if(json_error.error == QJsonParseError::NoError) { //判断是否是对象,然后开始解析数据 if(document.isObject()) { QJsonObject obj = document.object(); QString error_code; //解析 if(obj.contains("result")) { QJsonObject obj1=obj.take("result").toObject(); int words_block_count; QString text=""; if(obj1.contains("words_block_count")) { words_block_count=obj1.take("words_block_count").toInt(); // text=QString("识别到%1行文本.\n").arg(words_block_count); } if(obj1.contains("words_block_list")) { QJsonArray array=obj1.take("words_block_list").toArray(); for(int i=0;i<array.size();i++) { QJsonObject obj2=array.at(i).toObject(); if(obj2.contains("words")) { text+=obj2.take("words").toString(); text+="\n"; } } } ui->plainTextEdit->setPlainText(text); } } } } } /* 功能: 获取token */ void Widget::GetToken() { //表示获取token function_select=3; QString requestUrl; QNetworkRequest request; //设置请求地址 QUrl url; //获取token请求地址 requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens") .arg(SERVER_ID); //自己创建的TCP服务器,测试用 //requestUrl="http://10.0.0.6:8080"; //设置数据提交格式 request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8")); //构造请求 url.setUrl(requestUrl); request.setUrl(url); QString text =QString("{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":" "{\"user\":{\"domain\": {" "\"name\":\"d73ef34-6aa9-4120-aebe-c9e03375af1b"},\"name\": \")d73ef34-6aa9-4120-aebe-c9e03375af1b",\"password\": \"\"}}}," "\"scope\":{\"project\":{\"name\":\"Id73ef34-6aa9-4120-aebe-c9e03375af1b"}}}}") .arg(MAIN_USER) .arg(IAM_USER) .arg(IAM_PASSWORD) .arg(SERVER_ID); //发送请求 manager->post(request, text.toUtf8()); } //粘贴图片 void Widget::on_pushButton_copy_clicked() { QClipboard *clipboard = QApplication::clipboard(); const QMimeData *mimeData = clipboard->mimeData(); if (mimeData->hasImage()) { //将图片数据转为QImage QImage img = qvariant_cast<QImage>(mimeData->imageData()); if(!img.isNull()) { ui->widget->SetImage(img); } } } //获取图片里的文字信息 void Widget::getTextInfo(QImage image) { function_select=2; QString requestUrl; QNetworkRequest request; //存放图片BASE64编码 QString imgData; //设置请求地址 QUrl url; //人脸搜索请求地址 requestUrl = QString("https://ocr.%1.myhuaweicloud.com/v2/%2/ocr/general-text") .arg(SERVER_ID) .arg(PROJECT_ID); //设置数据提交格式 request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8")); //将图片进行Base64编码 imgData = QString(toBase64(image)); //编码后的图片大小不超过2M //设置token request.setRawHeader("X-Auth-Token",Token); //构造请求 url.setUrl(requestUrl); request.setUrl(url); QString post_param=QString ("{" "\"image\": \"d73ef34-6aa9-4120-aebe-c9e03375af1b"" "}").arg(imgData); //发送请求 manager->post(request, post_param.toUtf8()); }
-
前面文章介绍了Linux下进程的创建,管理,陆续介绍了进程间通信的方式:管道、内存映射、共享内存等。这篇文章继续介绍Linux的进程间通信方式消息队列。1. 消息队列介绍消息队列通过名字字面意思理解就是队列排队-和平常超市买东西排队付款一样结构,消息队列与FIFO很相似,都是一个队列结构,都可以有多个进程往队列里面写信息,多个进程从队列中读取信息。但FIFO需要读、写的两端事先都打开,才能够开始信息传递工作。而消息队列可以事先往队列中写信息,需要时再打开读取信息。注意事项:消息队列属于顺序队列形式的结构,向队列里写的每一条消息,会追加到队列后面,读取一个就从队列里消除一个。写函数不会阻塞,除非队列里存放的消息数量已经满了,才会导致写阻塞。读函数,从队列里读取不到数据时,会阻塞。查看当前系统所有的消息队列:[root@wbyq 20181005]# ipcs -q ------ Message Queues -------- 键值 消息队列ID 使用的字节数量 队里现存的消息数量 key msqid owner perms used-bytes messages 0x000004d3 0 root 666 65532 192 0x00003044 32769 root 666 65424 564 0x0a120534 65538 root 0 2352 21 0x0a00000f 196611 root 0 65520 2730 0xffffffff 163844 root 0 0 0 查看消息队列的详细信息:[root@wbyq 20181005]# ipcs -l ------ Shared Memory Limits -------- max number of segments = 4096 max seg size (kbytes) = 4194303 max total shared memory (kbytes) = 1073741824 min seg size (bytes) = 1 ------ Semaphore Limits -------- max number of arrays = 128 max semaphores per array = 250 max semaphores system wide = 32000 max ops per semop call = 32 semaphore max value = 32767 ------ Messages: Limits -------- max queues system wide = 1736 【系统最多的消息队列数量(最多支持同时1736个消息队列)】 max size of message (bytes) = 65536 【单个消息的最大字节数】 default max size of queue (bytes) = 65536 【默认的单个队列的大小65536】 默认单个队列总字节大小为: 65535字节。 消息队列的最大字节数量和消息的最大条数加起来的总字节数不能超过65535字节。**System V IPC机制消息队列相关的函数接口: ** 这里先列出所有函数,下面会详细介绍每个函数的用法。#include <sys / types.h> #include <sys / ipc.h> #include <sys / msg.h> int msgget(key_t key, int msgflg); int msgsnd(int msqid, struct msgbuf * msgp, size_t msgsz, int msgflg); ssize_t msgrcv(int msqid, struct msgbuf * msgp, size_t msgsz, long msgtyp, int msgflg); int msgctl(int msqid, int cmd, struct msqid_ds * buf);2. 消息队列相关函数介绍2.1 msgget函数函数原型:#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);功能 msgget用于创建和访问一个消息队列。 参数 (1) key:是唯一标识一个消息队列的关键字,如果为IPC_PRIVATE(值为0,用创建一个只有创建者进程才可以访问的消息队列),表示创建一个只由调用进程使用的消息队列,非0值的key(可以通过ftok函数获得)表示创建一个可以被多个进程共享的消息队列; (2) msgflg:指明队列的访问权限和创建标志,创建标志的可选值为IPC_CREAT和IPC_EXC,如果单独指定IPC_CREAT,msgget要么返回新创建的消息队列id,要么返回具有相同key值的消息队列id;如果IPC_EXCL和IPC_CREAT同时指明,则要么创建新的消息队列,要么当队列存在时,调用失败并返回-1。/*1. 创建消息队列*/ int msgid = msgget((key_t)1235,0666 | IPC_CREAT);返回值 成功执行时,返回消息队列标识值(0也是成功的)。失败返回-1,errno被设为以下的某个值。EACCES:指定的消息队列已存在,但调用进程没有权限访问它,而且不拥有CAP_IPC_OWNER权能 EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志 ENOENT:key指定的消息队列不存在同时msgflg中不指定IPC_CREAT标志 ENOMEM:需要建立消息队列,但内存不足 ENOSPC:需要建立消息队列,但已达到系统的最大消息队列容量2.2 msgsnd和msgrcy函数原型:#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);功能函数msgsnd和msgrcy用来将消息添加到消息队列中和从一个消息队列中获取信息。参数(1)msgid:指明消息队列的ID; 通常是msgget函数成功的返回值。(2)msgbuf:是消息结构体,它的长度必须小于系统规定的上限,必须以一个长整型成员变量开始,接收函数将用这个成员变量来确定消息的类型。必须重写这个结构体,其中第一个参数类型不能改,其他可以自定义。如下:struct msgbuf { long mtype; /* type of message */ char mtext[1]; /* message text */ };字段mtype是用户自己指定的消息类型(通常是1—5中的任意一个数值),该结构体第2个成员仅仅是一种说明性的结构,实际上用户可以使用任何类型的数据,就是消息内容;(3)msgsz是消息体的大小,每个消息体最大不要超过4K; 不含消息类型占用的4个字节,即mtext的长度(4)msgtyp有3种选项:msgtyp == 0 接收队列中的第1个消息,不区分消息类型 msgtyp > 0 接收对列中的第1个类型等于msgtyp的消息 msgtyp < 0 接收其类型小于或等于msgtyp绝对值的第1个最低类型消息(5)msgflg有3种选项:0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列 IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回 IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。2.3 msgctl函数原型:#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf);功能msgctl是消息队列的控制函数,常用来删除消息队列。参数(1)msqid:由msgget返回的消息队列标识符。(2)cmd:通常为IPC_RMID表示删除消息队列。(3)参数buf通常为NULL。通过命令查看系统消息信息(1)ipcs -q 命令查看系统的消息队列(2)ipcs -m查看系统的共享内存(3)ipcs -s 查看系统的信号量集。3. 案例代码: 消息队列示例1下面两个程序分别编译,依次运行,不分先后顺序,就可以看到效果了。3.1 发送消息#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/stat.h> #define BUFFER 255 struct msgtype { //重新定义该结构体 long mtype; //第一个参数类型不变 char buf[BUFFER]; }; int main(int argc,char **argv) { if(argc!=3) { printf("./app <消息> <消息类型-整数>\n"); return 0; } /*1. 创建消息队列*/ int msgid = msgget((key_t)1235,0666 | IPC_CREAT); /*2. 向消息队列发送消息*/ struct msgtype msg; memset(&msg,0,sizeof(struct msgtype)); msg.mtype = atoi(argv[2]); //给结构体的成员赋值 strncpy(msg.buf,argv[1],BUFFER); msgsnd(msgid,&msg,sizeof(struct msgtype),0); return 0; }3.2 读取消息#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/stat.h> #include <sys/msg.h> #define BUFFER 255 struct msgtype { long mtype; char buf[BUFFER]; }; int main(int argc,char **argv) { if(argc!=2) { printf("./app <消息类型-整数>\n"); return 0; } /*1. 创建消息队列-如果存在就打开消息队列*/ int msgid = msgget((key_t)1235, 0666 | IPC_CREAT); //获得消息队列 struct msgtype msg; memset(&msg,0,sizeof(struct msgtype)); while(1) { /*2. 从消息队列里读取指定消息类型*/ msgrcv(msgid,&msg,sizeof(struct msgtype),atoi(argv[1]),0); printf("读取的消息: %s\n", msg.buf); } return 0; }4. 案例代码: 消息队列基本使用下面两个例子,一个例子用于创建队列,并向队列里写数据,另一个例子从队列里读取数据。4.1 向队列写入消息程序运行需要传入两个额外的参数。一个是消息的类型,一个是具体的内容。./app 1 hello ./app 2 123456789示例代码:#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/wait.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h> struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1024]; /* message data */ }; int main(int argc,char **argv) { if(argc!=3) { printf("./app <消息类型> <消息内容>\n"); return 0; } /*1. 创建消息队列*/ int msqid=msgget(123456,0666|IPC_CREAT); /*2. 向消息队列里加入数据*/ struct msgbuf msg_buf; msg_buf.mtype=atoi(argv[1]); //消息类型 1、2、3、4、5 strcpy(msg_buf.mtext,argv[2]); //消息内容 msgsnd(msqid,&msg_buf,sizeof(struct msgbuf),0); printf("消息发送成功:%s,%s\n",argv[1],argv[2]); return 0; }4.2 从队列读取消息#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/wait.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h> struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1024]; /* message data */ }; int main(int argc,char **argv) { if(argc!=2) { printf("./app <消息类型>\n"); return 0; } /*1. 创建消息队列*/ int msqid=msgget(123456,0666|IPC_CREAT); /*2. 从消息队列里取出数据*/ struct msgbuf msg_buf; msgrcv(msqid,&msg_buf,sizeof(struct msgbuf),atoi(argv[1]),0); printf("消息读取成功:%d,%s\n",msg_buf.mtype,msg_buf.mtext); return 0; }
-
1. 进程间通信方式介绍这篇文章介绍Linux下进程的间的通信方式,常用的方式如下:1. socket—网络通信 2. 管道---无名管道—命名管道---文件--FIFO 3. 消息队列 4. 共享内存 5. 信号量集 6. 信号—signal捕获信号---kill命令发送信号 int kill(pid_t pid, int sig);2. 标准流管道标准流管道像文件操作有标准io流一样,管道也支持文件流模式。用来创建连接到另一进程的管道popen和pclose。 函数原型:#include <stdio.h> FILE* popen(const char* command, const char* open_mode); int pclose(FILE* fp);popen用于启动进程,用法含义与fopen类似,第二个参数填权限,支持填"r"和"w"。pclose用于关闭进程,释放资源。popen启动进程之后可以直接与启动的进程间通信,比较方便。**示例代码: **从标准管道流中读取 打印/etc/profile的内容:#include <stdio.h> #include <stdlib.h> int main() { FILE *file=NULL; size_t len=0; char buff[1024+1]; file=popen("cat /etc/profile","r"); //执行cat /etc/profile命令,读方式。 if(file!=NULL) { len=fread(buff,1,1024,file); //读数据 buff[len]='\0'; printf("len=Þbd86b09-1ee3-4acf-8ecf-7bf7c1c73160n %s\n",len,buff); } pclose(file); //等待线程结束。 return 0; }3. 无名管道无名管道用于有亲戚关系的进程间通信。 比如: 兄弟进程、父子进程等。#include <unistd.h> int pipe(int fds[2]);pipe函数用于创建一个无名管道,如果成功,fds[0]就存放可读的文件描述符,fds[1]就存放可写文件描述符。返回值: 0表示成功,-1表示失败。无名管道的特点:只能在亲缘关系进程间通信(父子或兄弟)半双工(固定的读端和固定的写端)虚拟管道文件是一个存在内存的特殊文件,可以用read、write函数进行操作。这里说的管道,就像一条水管,有两个端口,一端进水,另一端出水。管道也有两个端口,分别是读端和写端,进水可看成数据从写端被写入,出水可看数据从读端被读出。在程序里分别就对应了read和write函数。示例代码:#include <stdio.h> #include <unistd.h> #include <string.h> int main(int argc,char **argv) { int fds[2]; /*1. 创建无名管道:得到管道读写文件描述符 fds[0]和读端相对应, fds[1]和写端相对应*/ pipe(fds); /*2. 创建子进程*/ pid_t pid; pid=fork(); if(pid==0) //子进程 { char buff[100+1]; int cnt; //从管道的读端读取数据 cnt=read(fds[0],buff,100); //带阻塞功能 buff[cnt]='\0'; printf("子进程收到的数据:%d,%s\n",cnt,buff); } else //父进程 { char buff[]="1234567890"; //向管道的写端写数据 write(fds[1],buff,strlen(buff)); //等待子进程结束 wait(NULL); printf("父进程正常结束.\n"); } return 0; }4. 命名管道无名管道只能在亲缘关系的进程间通信大大限制了管道的使用,有名管道突破了这个限制,通过指定路径名的形式实现不相关进程间的通信,因为命名管道通信使用的管道是一个实体文件,在磁盘上的存在的,而无名管道是存在内存中的虚拟文件,其他进程无法访问,导致没有关联的进程无法进行通信。4.1 在命令行如何创建管道文件?[wbyq@wbyq test]$ mkfifo test.fifo [wbyq@wbyq test]$ ls test.fifo [wbyq@wbyq test]$ ls -l 总用量 0 prw-rw-r--. 1 wbyq wbyq 0 10月 15 15:29 test.fifo4.2 在命令行演示两个相关的进程通过进行管道文件进行通信4.3 创建fifo文件的函数1. 创建FIFO文件 #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 参数: pathname:创建的FIFO文件的全路径名; mode:文件访问权限,比如0666。 返回值:如果创建成功,则返回0,否则-1。 2. 删除FIFO文件 #include <unistd.h> int unlink(const char *pathname); 3. 用命令创建和删除FIFO文件 用命令mkfifo创建。 用命令unlink删除。4.4 创建写端: FIFO#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc,char **argv) { if(argc!=2) { printf("./a.out <fifo文件--写>\n"); return 0; } int fd=open(argv[1],O_WRONLY); if(fd<0) { printf("%s 文件打开失败.\n",argv[1]); return 0; } char buff[]="1234567890"; write(fd,buff,strlen(buff)); close(fd); return 0; } 示例代码: 进程B负责读数据 #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc,char **argv) { if(argc!=2) { printf("./a.out <fifo文件--读>\n"); return 0; } int fd=open(argv[1],O_RDONLY); if(fd<0) { printf("%s 文件打开失败.\n",argv[1]); return 0; } char buff[100+1]; int cnt; cnt=read(fd,buff,100); buff[cnt]='\0'; printf("buff=%s\n",buff); close(fd); return 0; }4.5 创建读端: FIFO在命令行演示两个相关的进程通过进行管道文件进行通信.代码创建读端,读取数据:#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fd=0; int len=0; char buff[100]; fd=open("myfifo",O_RDONLY); len=read(fd,buff,100); buff[len]='\0'; printf("read: %s\n",buff); return 0; }
-
介绍的C语言知识如下:宽字符定义使用、gtk图像框架安装与测试、动态数组定义、switch的...符号用法、windows下操作目录相关函数、可变形参的函数定义方式以及数据的提取办法,scanf函数在widows下vs里的使用问题、windows下system执行命令调用系统可执行文件运行。一、Linux下C语言代码定义宽字符问题用多个字节来代表的字符称之为宽字符。宽字符是双字节多语言字符代码。在当今的全球计算业内使用的大多数字符(包括技术符号和特殊的发布字符),都可以根据 Unicode 规范表示为宽字符形式。无法以 1 个宽字符表示的字符可以通过 Unicode 的代理项功能以 Unicode 对表示。由于每个宽字符总是以固定的 16 位大小表示,因此使用宽字符可以简化使用国际字符集进行的编程。#include <stdio.h>#include <string.h>#include <wchar.h>int main(void){ /*文本编辑器必须是在UTF-8编码下才可以正常编译*/ char *p="1234567890中国"; //16 char *p2="中国中国"; //12 UTF-8编码下一个中文3个字节 wchar_t *wp=L"1234567890中国"; //12 wchar_t *wp2=L"中国中国"; //4 printf("strlen p:Õ6be90d7-ec09-4ca3-867b-6a782bdc474cn",strlen(p)); printf("strlen p2:Õ6be90d7-ec09-4ca3-867b-6a782bdc474cn",strlen(p2)); printf("wcslen p:Õ6be90d7-ec09-4ca3-867b-6a782bdc474cn",wcslen(wp)); //返回值是12 printf("wcslen p2:Õ6be90d7-ec09-4ca3-867b-6a782bdc474cn",wcslen(wp2)); //返回值是4 return 0;}二、C语言动态数组定义示例(C99)C99支持一种名为变长数组的结构来方便程序员。C++也提供了一种长度可在程序运行时确定的数组类型:动态数组。声明格式为:(声明 int 类型的数组)#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <dirent.h> //ls /456/789/*int main(int argc,char **argv){ if(argc!=2) { printf("正确参数:./a.out <目录路径>\n"); return 0; } /*1. 打开目录*/ DIR *dir; struct dirent *dir_info; dir=opendir(argv[1]); if(dir==NULL) { printf("%s目录打开失败!\n",argv[1]); return 0; } /*2. 遍历目录*/ while(dir_info=readdir(dir)) { if(strcmp(dir_info->d_name,".")==0||strcmp(dir_info->d_name,"..")==0) { continue; } //1. 定义动态数组,存放完整路径 char str[strlen(dir_info->d_name)+strlen(argv[1])+1]; //2. 拷贝目录路径 strcpy(str,argv[1]); //3. 拼接文件名称 strcat(str,dir_info->d_name); //4. 执行正常程序 printf("%s\n",str); /*..视频播放器(str)、音乐播放器、数码相册.......*/ } /*3. 关闭目录*/ closedir(dir); return 0;}三、switch多分支结构新特性(C99)ANSI于1989年制定了C编程语言的官方标准,并于1990年成为国际标准,C语言规范在一段时间内保持相对静态,而C ++继续发展,主要是在其自身的标准化工作中。规范修正案1在1995年为C制定了一个新标准,但只是为了纠正1989年标准的一些细节,并为国际字符集增加了更广泛的支持。该标准在20世纪90年代后期进行了进一步修订,导致1999年出版了ISO / IEC 9899:1999,并于2000年5月作为ANSI标准采用。该标准版本定义的语言通常称为“ C99" 。#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <dirent.h> //ls /456/789/*int main(int argc,char **argv){ if(argc!=2) { printf("正确参数:./a.out <数字>\n"); return 0; } int num=atoi(argv[1]); switch(num) { case 1 ... 5: printf("你输入的是1到5的范围数据!\n"); break; case 10 ... 50: printf("你输入的是10到50的范围数据!\n"); break; case 60 ... 80: printf("你输入的是60到80的范围数据!\n"); break; case 'a' ... 'z': printf("你输入的是a到z的范围数据!\n"); break; default: printf("范围不满足!\n"); break; } return 0;}四、GTK库安装说明在linux下想使用C语言做一个图形化的界面可以选择GTK。1、安装gcc/g++/gdb/make 等基本编程工具sudo apt-get install build-essential2、安装 libgtk2.0-dev libglib2.0-dev 等开发相关的库文件sudo apt-get install gnome-core-devel3、用于在编译GTK程序时自动找出头文件及库文件位置sudo apt-get install pkg-config4、安装 devhelp GTK文档查看程序sudo apt-get install devhelp5、安装 gtk/glib 的API参考手册及其它帮助文档sudo apt-get install libglib2.0-doc libgtk2.0-doc6、安装基于GTK的界面GTK是开发Gnome窗口的c/c++语言图形库sudo apt-get install glade libglade2-dev7、安装gtk2.0 或者 将gtk+2.0所需的所有文件统通下载安装完毕sudo apt-get install libgtk2.0-dev安装完毕后就可以开始写第一个简单的程序了!五、windows下改变控制台#include "stdio.h"int main(){ //printf("\33[%dm",100); //printf("\33[x;yH"); //将输出坐标定位在第x行,第y列 //printf("\33[%d;%dH",10,10);//将输出坐标定位在第x行,第y列 //printf("\33[10;10H"); printf("hello world!\n");} /*一、设置控制台 1、更改屏幕的背景色,字体颜色 printf("\33[%dm", i); 30<= i <=37 设置字体颜色 30黑,31红,32绿,33黄,34蓝,35紫,36深绿,37白 40<= i <=47 设置背景颜色 40黑,41红,42绿,43黄,44蓝,45紫,46深绿,47白 2、任意指定屏幕的输出坐标 printf("\33[x;yH"); 将输出坐标定位在第x行,第y列 printf("\33[%d;%dH", x,y); 将输出坐标定位在第x行,第y列*/六、strchr字符串查找函数#include "stdio.h"#include "string.h"int main(){ char buff1[]="123 456 789"; char buff2[]="abc,uuu,oooo"; printf("%s\n",strchr(buff1,' ')); printf("%s\n",strchr(buff2,',')); return 0;}/* * * #include <string.h> * char *strchr(const char *s, int c); * * */七、windows下C语言操作目录获得当前工作目录char* _getcwd( char *buffer, int maxlen );更改当前工作目录int _chdir( const char *dirname );文件遍历(查找)long _findfirst( char *filespec, struct _finddata_t *fileinfo );int _findclose( long handle );关闭搜寻句柄并释放相应资源创建目录int _mkdir( const char *dirname );删除目录int _rmdir( const char *dirname );其他操作int _access( const char *path, int mode );int _chdrive( int drive ); 功 能 : 更改当前工作驱动器char* _getdcwd( int drive, char *buffer, int maxlen ); 功 能 : 获得指定驱动器的当前工作路径.八、windows下调用应用程序#include <windows.h>system("notepad.exe");九、short整型的scanf正确输入格式#pragma warning(disable:4996)#include <stdio.h>#include <string.h>struct stu{ short grade; //如果这里是短整型,scanf输入必须是%hd, 不能使用%d, %d是int类型,按照4个字节输入。会导致空间溢出到下面的name数组里. char name[21];};int main(){ struct stu STU = {11,"小米"}; printf("结构体大小:Õ6be90d7-ec09-4ca3-867b-6a782bdc474cn",sizeof(struct stu)); //STU.grade = 12; //年龄 //memcpy(STU.name,"小明",5); //姓名 printf("输入姓名:"); scanf("%s", STU.name); printf("输入年龄:"); scanf("%hd",&STU.grade); printf("年龄:%hd\n", STU.grade); printf("姓名:%s\n", STU.name); return 0;}十、C语言结构体位段位段(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间。含有位段的结构体(联合体)称为位段结构。采用位段结构既能够节省空间,又方便于操作。位段的定义格式为: type [var]: digits其中type只能为int,unsigned int,signed int三种类型(int型能不能表示负数视编译器而定,比如VC中int就默认是signed int,能够表示负数)。位段名称var是可选参数,即可以省略。digits表示该位段所占的二进制位数。那么定义一个位段结构可以像下面这段代码去定义:struct node{ unsigned int a:4; //位段a,占4位 unsigned int :0; //无名位段,占0位 unsigned int b:4; //位段b,占4位 int c:32; //位段c,占32位 int :6; //无名位段,占6位};十一、C语言可变形参示例代码(1)示例代码#include <stdio.h>#include <string.h>#include <sys/types.h>#include <dirent.h>#include <stdlib.h>#include <stdarg.h>void func(int a,...);int main(int argc,char **argv){ //func(123,456,789,"8888"); func(123); return 0;}//可变形参一般都是约定个数 或者根据第一个参数,决定个数void func(int a,...){ int b=0,c=0; char *p=NULL; va_list ap; va_start(ap,a); b=va_arg(ap,int); c=va_arg(ap,int); p=va_arg(ap,char*); printf("a=%d,b=%d,c=%d,p=%s\n",a,b,c,p); va_end(ap);}(2)示例代码#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdlib.h>#include <stdarg.h> void foo(char *fmt, ...);int main(int argc,char **argv){ foo("%d,%s,%f,Å6be90d7-ec09-4ca3-867b-6a782bdc474cn",123,"456",789.123,'A'); return 0;}void foo(char *fmt, ...){ va_list ap; int d; char c, *s; double f; /*1. 初始化形参列表*/ va_start(ap, fmt); while(*fmt) switch(*fmt++) { case 's': /* string */ s = va_arg(ap,char *); printf("string %s\n", s); break; case 'd': /* int */ d = va_arg(ap, int); printf("int Õ6be90d7-ec09-4ca3-867b-6a782bdc474cn", d); break; case 'c': /* char */ c = (char) va_arg(ap, int); printf("char Å6be90d7-ec09-4ca3-867b-6a782bdc474cn", c); break; case 'f': /* char */ f = (char) va_arg(ap,double); printf("double õ6be90d7-ec09-4ca3-867b-6a782bdc474cn",f); break; } /*释放参数列表,结束读取*/ va_end(ap);}
-
1、在华为盒子上测试通过的代码,在200加速模块(嵌入式板子上)上测试失败,显示的是sudo的问题。(固件版本:1.0.13、nnrt版本:5.0.4 Python:3.7.5, 都在root下安装的, import acl正常, npu-smi info 正常)
上滑加载中
推荐直播
-
空中宣讲会 2025年华为软件精英挑战赛
2025/03/10 周一 18:00-19:00
宸睿 华为云存储技术专家、ACM-ICPC WorldFinal经验 晖哥
2025华为软挑赛空中宣讲会重磅来袭!完整赛程首曝+命题天团硬核拆题+三轮幸运抽奖赢参赛助力礼包,与全国优秀高校开发者同台竞技,直通顶尖赛事起跑线!
回顾中 -
华为开发者空间玩转DeepSeek
2025/03/13 周四 19:00-20:30
马欣 华为开发者布道师
同学们,想知道如何利用华为开发者空间部署自己的DeepSeek模型吗?想了解如何用DeepSeek在云主机上探索好玩的应用吗?想探讨如何利用DeepSeek在自己的专有云主机上辅助编程吗?让我们来一场云和AI的盛宴。
即将直播 -
华为云Metastudio×DeepSeek与RAG检索优化分享
2025/03/14 周五 16:00-17:30
大海 华为云学堂技术讲师 Cocl 华为云学堂技术讲师
本次直播将带来DeepSeek数字人解决方案,以及如何使用Embedding与Rerank实现检索优化实践,为开发者与企业提供参考,助力场景落地。
去报名
热门标签