通信原理课设(gec6818) 002:文件io+文件偏移+LCD+内存映射
目录
1、文件io
文件io分为两种:
系统io:系统提供给外面的一个接口,让程序员能通过这个接口去操作系统里面的文件,系统io意味着系统不一样,那么接口操作就会不一样。
标准io:由标准C库(第三方库)提供的接口函数,比如:fopen、fread、fwrite、scanf、printf...
这个标准io只是针对于普通文件。由于标准io带缓冲区,因此它的效率要高一些,所以:普通文件建议使用标准io。
2、文件偏移
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数解释
fd:文件描述符
offset:偏移量 也就是你的光标要挪动多少个单位,单位是字节
这个参数可正可负,正代表向后负代表向前
whence:基准定位
SEEK_SET 基于文件的开头,这个时候如果offset负数,表示继续往前,但是前面是没有数据,这个时候文件会留下空洞。从原来的文件结尾到新写入数据间的这段空间被成为文件空洞。有时候空洞是好事,但是会造成资源的浪费。
SEEK_CUR 基于光标的当前位置
SEEK_END 基于文件的末尾,这个时候如果offset正数,表示继续往后,注意后面是没有数据,这个时候文件会留下空洞返回值:成功返回光标当前的位置到文件开头所有的字节数
所以求文件大小:unsigned int filesize = lseek(fd,0x00,SEEK_END);
失败返回-1,同时errno被设置。
3、LCD屏幕
gec6918开发板上面的屏幕是800*480的点阵,每个点是3个颜色 --- rgb,每一个点是由一个uint(4个字节)型数据去表示。
数据的表示是有严格的顺序的(argb):
最高字节是透明度(a)
次高字节是红色(r)
次低字节是绿色(g)
最低字节是蓝色(b)
我们现在既不模拟3D 也不需要透明,所有这个透明度没有什么作用,随便你弄多少都可以,但是这个字节你还是不能丢。现在我们需要让我们的屏幕成为一个什么样子的颜色,我只需要往屏幕里面写入800 * 480个int值就可以了,用一个值来表示一个颜色 我们叫颜色的量化。
unsigned int color[800 * 480];
int i;
for(i = 0;i < 800*480;i++)
{
color[i] = 0xff0000;
}
LCD屏也是一个文件,只需要将color这个数组写入到这个屏幕文件即可。屏幕的文件为 /dev/fb0
4、练习1
1、实现一个文件拷贝功能
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
//写一个copy的功能函数
void copyfile(const char *from,const char * to)
{
//from是必须要存在的
int fd_from = open(from,O_RDONLY);
if(-1 == fd_from)
{
perror("open from error");
exit(1);
}
//to 是可能不存在的 如果不存在我需要创建 如果存在我需要截短
int fd_to = open(to,O_WRONLY | O_CREAT | O_TRUNC,0664);
if(-1 == fd_to)
{
perror("open to error");
close(fd_from);
exit(2);
}
//执行搬砖的任务 你需要一个小车
//unsigned char buf[1024];
unsigned char * ptr = malloc(1024);
//memset(ptr,0,1024);
//我们要一直搬砖 知道砖没有
int r;
int w;
unsigned int filesize = lseek(fd_from,0x00,SEEK_END);//
lseek(fd_from,0x00,SEEK_SET);//弄完需要回到开头 重新进行copy操作
int sum = 0;
while(1)
{
r = read(fd_from,ptr,1024);
if(-1 == r)
{
perror("read error");
break;
}
else if(0 == r)//你读成功了 但是读的字节是0 说明你弄完了
{
printf("copy over!!!\n");
break;
}
else//这里就读到内容了
{
w = write(fd_to,ptr,r);
sum += w;
if(w < r)//丢字节了
{
perror("write error");
break;
}
//求百分比 相当于文件拷贝的进度条
printf("\r\033[K%d %%",(int)((double)sum / filesize * 100));
fflush(stdout);
}
}
close(fd_from);
close(fd_to);
}
int main(int argc,char * argv[])
{
if(argc < 3)
{
printf("参数不对,不能执行复制操作\n");
return -1;
}
copyfile(argv[1],argv[2]);
return 0;
}
2、 实现你的屏幕切屏、循环切颜色
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int r, g, b;
int main()
{
//1 打开文件
int fd = open("/dev/fb0", O_RDWR);
if (-1 == fd)
{
perror("lcd open error");
return -1;
}
unsigned int red_color[800 * 480],green_color[800*480], bule_color[800 * 480];
int i;
for (i = 0; i < 800 * 480; i++)
{
red_color[i] = 0xff0000;
green_color[i] = 0xff00;
bule_color[i] = 0xff;
}
if (r != 800 * 480 * 4)
{
perror("write error");
}
if (g != 800 * 480 * 4)
{
perror("write error");
}
if (b != 800 * 480 * 4)
{
perror("write error");
}
while(1)
{
r = write(fd, green_color, 800 * 480 * 4);
sleep(5);
lseek(fd, 0x00, SEEK_SET);
g = write(fd, red_color, 800 * 480 * 4);
sleep(5);
lseek(fd, 0x00, SEEK_SET);
b = write(fd, bule_color, 800 * 480 * 4);
sleep(5);
lseek(fd, 0x00, SEEK_SET);
}
close(fd);
return 0;
}
补充:延时 :sleep(1); -> 秒级延时;usleep() -> 微秒级延时
将该代码刷到gec6818板子上后,会发现LCD屏上有条纹状 ,这是因为(800*480)的数组复制了两遍,以及应用态和内核态之间的切换造成了效率低下,我们可以通过内存的映射来解决这个问题。
5、内存映射
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
addr:你要将文件映射到内存的哪个地址,一般我们是难得去得到这个地址的,因此我们需要让操作系统给我指定 填NULL即可
length:长度 你要映射多大,单位是字节, 要以页为单位 1页 = 4K,800 * 480 * 4
prot:权限 一般要根据fd的权限 PROT_READ 可读
PROT_WRITE 可写
PROT_READ | PROT_WRITE 读写
flags:映射的标志
MAP_SHARED 公有映射
对于内存的操作可以直接影响文件,一般选这个
MAP_PRIVATE 私有映射
开辟一个内存你自己玩,不会直接影响文件
fd:你要映射哪个文件 就是一个文件描述符
- offset:偏移量 单位是字节 但是以页为单位,填0表示不偏移
返回值:
成功返回映射后的首地址,我们操作这个地址就可以直接去影响文件了(MAP_SHARED),失败返回MAP_FAILED ,同时errno被设置
操作完成之后,将内存归还系统:
int munmap(void *addr, size_t length);
addr:你映射的那个首地址
length:你要解除多大,一般映射多大就解除多大
返回值:成功返回0,失败返回-1,同时errno被设置