鸿蒙移植i.mx6ull(五) 移植概述

1.框架

在这里插入图片描述

鸿蒙是一套完整的、普通人可以直接使用的操作系统,跟Windows、安卓、IOS类似。
常见的错误观点是把鸿蒙跟Linux放在一起来对比,这不对:

  • Linux只是一个内核,普通人无法使用

  • 还需要在Linux之上安装各类程序

  • 比如Ubuntu等发行版,它们在Linux内核之上,还有桌面、各类办公软件

  • 鸿蒙支持多种内核:LinuxLiteos(又分为Liteos-aLiteos-m)

  • 在内核之上,鸿蒙还有各种子系统,在子系统之上又有桌面等软件

2. 嵌入式软件系统的组成

在这里插入图片描述

我们可以把内核之上的软件,简单称为APP(实际上还可以细分,比如各类子系统、桌面等APP)。
启动内核,并不复杂,使用U-boot即可。

3. 移植最小系统要做的几件事

串口相关

  • 打印(只是打印调试信息)
  • 串口驱动(可发可收,APP执行printf时可以从串口打印,所以需要驱动)

在这里插入图片描述

  • MMU(Memory Management Unit,内存管理单元)的设置:虚拟地址与物理地址

完善中断子系统

  • 提供系统tick时钟
  • 为串口驱动实现基于中断的读取字符函数
  • 实现存储设备驱动程序
  • 在存储设备上烧录文件系统

3.1 串口相关

Linux的串口驱动相比,鸿蒙的串口驱动极大简化了。
对于输出:不使用中断,直接使用查询方式输出。
对于输入:使用中断,我们只需要提供底层硬件相关的代码。
要注意:使用的是虚拟地址。

3.2 MMU设置

MMU有2大功能:

在这里插入图片描述

3.2.1 权限管理

  • 比如可以把进程A、B的地址空间完全隔离开,它们互不影响
  • 写得差的进程、有恶意的进程,不能影响到其他进程
  • 用户程序、内核地址空间完全隔离开:不允许用户直接访问硬件

3.2.2 地址映射

  • 使能MMU后,CPU发出的地址被称为"虚拟地址",它不是直接发送给硬件,而是发给MMU

  • MMU根据页表

    • 进行权限判定
    • 转换为物理地址,发给外设

    运行app1时,CPU发出的addr,通过MMU映射到paddr1;
    运行app2时,CPU发出的同一个addr,通过MMU映射到paddr2;
    虽然app1、app2使用的地址相同,但是对应的内存不同

  • 在移植过程中,我们不需要关注“权限”,只需要关注“地址映射”

3.3 中断子系统

操作系统跟单片机程序最大的区别,就是多任务,也就是同时运行多个程序。
同时,对人类来说是这样的,多个程序好像可以同时运行,实际上它们是轮流运行。

3.3.1 操作系统"同时"运行多个任务

轮流运行:

在这里插入图片描述

3.3.2 串口接收数据的中断

使用串口接收数据时,如果使用"查询"方式,低效并且费电。 一般都使用中断方式。

3.4 存储设备的驱动程序

板子上一般都有EMMCSD/TF卡、Nor FlashNand Flash等存储设备。 Nor FlashNand Flash的驱动程序相对简单,但是这些设备比较少见了。 而EMMCSD/TF卡的驱动程序又太复杂,足够出一个专题了。
我们聚焦在最小系统的移植,先把流程走通:用内存来模拟Flash

3.5 根文件系统

光有存储设备还不行,上面需要有文件:这就是根文件系统。
一个程序要能运行,出了你写出的程序本身,还需要其他库,比如printf就不是你写的,它在库文件里。 根文件系统里会有这些内容:

  • 程序
  • 配置文件
  • 用户数据(可选)
  • 驱动程序(可选)

4. 想做更多

  • 为有更好的人机交互可以移植LCD、触摸屏驱动
  • 为了方便开发,移植EMMC驱动、网卡驱动
  • 要接各类外设,还需要I2CSPIGPIOUART驱动
  • 摄像头、声卡驱动

5. 基础知识

移植内核对技术的要求比较全面、比较细致。

5.1 单片机相关的知识

  • 栈的作用

  • 加载地址、链接地址

  • 重定位

  • 几个简单的硬件知识 * 串口 * 定时器

  • 中断的概念

5.2 Linux操作相关的知识

  • Linux常用命令
  • 简单的脚本:脚本就是把命令写在一个文件里
  • GCC编译命令
  • KconfigMakefile

5.3 芯片相关知识

  • 能阅读芯片手册(英文)
  • 移植最小系统时,涉及的手册内容不多
  • 能看懂硬件原理图
  • 移植最小系统时,涉及的原理图内容不多

6. 驱动程序知识

对于只有单片机知识的人来说,怎么去操作硬件?

  • 直接读写寄存器
  • 使用库函数

RTOS中,本质也是去读写寄存器,但是需要有统一的驱动程序框架。 所以:RTOS驱动 = 驱动框架 + 硬件操作

6.1 以点灯为例

6.1.1 硬件原理

在这里插入图片描述

6.1.2 单片机点灯

  • 方法1:直接读写寄存器

在这里插入图片描述

  • 方法2:使用厂家的HAL库
    在这里插入图片描述

6.1.3 Liteos-a/Linux怎么点灯

使用MMU时,一般APP与内核是相互隔离的。APP通过标准的open/read/write等文件操作函数去调用驱动程序。
如下图所示:

在这里插入图片描述

为何要多此一举?

  • 它们支持MMU(内存管理单元)
  • 用户程序跟内核是分隔开的,用户程序不能直接读写寄存器
  • 用户程序通过标准接口访问驱动程序
  • 基于这些内核的软件一般都比单片机软件复杂,术业有专攻
  • 不应该让写APP的人去看原理图、写驱动、写寄存器
  • 软件和硬件隔离,硬件再怎么变化,只需要改驱动,APP不需要改

6.2 怎么编写驱动程序

6.2.1 驱动程序的核心

LinuxLiteos-a的驱动程序时类似的,Liteos-a的更加精简。
既然APP使用驱动是调用open/read/write等接口,那么写驱动程序是最简单的方法就是提供对应的drv_open/drv_read/drv_write等函数。
这些函数放在一个结构体里:Linux对应file_operations结构体,Liteos-a对应file_operations_vfs结构体。

1. Linux

Linux中是定义一个file_operations结构体,如下:

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
	int (*show_fdinfo)(struct seq_file *m, struct file *f);
};
2. Liteos-a

Liteos-a中定义了一个file_operations_vfs结构体,如下:

struct file_operations_vfs
{
  /* The device driver open method differs from the mountpoint open method */

  int     (*open)(FAR struct file *filep);

  /* The following methods must be identical in signature and position because
   * the struct file_operations and struct mountp_operations are treated like
   * unions.
   */

  int     (*close)(FAR struct file *filep);
  ssize_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
  ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
  off_t   (*seek)(FAR struct file *filep, off_t offset, int whence);
  int     (*ioctl)(FAR struct file *filep, int cmd, unsigned long arg);
  int     (*mmap)(FAR struct file* filep, struct VmMapRegion *region);
  /* The two structures need not be common after this point */

#ifndef CONFIG_DISABLE_POLL
  int     (*poll)(FAR struct file *filep, poll_table *fds);
#endif
  int     (*unlink)(FAR struct inode *inode);
};

6.3 注册驱动程序

1. Linux

static struct file_operations hello_drv = {
	.owner	 = THIS_MODULE,
	.open    = hello_drv_open,
	.read    = hello_drv_read,
	.write   = hello_drv_write,
	.release = hello_drv_close,
};

int major = register_chrdev(0, "hello", &hello_drv);  /* /dev/hello */
static struct class *hello_class = class_create(THIS_MODULE, "hello_class");
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */

2. Liteos-a

static const struct file_operations_vfs g_helloDevOps = {
    .open   = hello_open,
    .close  = hello_close,
    .read   = hello_read,
    .write  = NULL,
    .seek   = NULL,
    .ioctl  = NULL,
    .mmap   = NULL,
    .unlink = NULL,
};

int ret = register_driver("/dev/hello", &g_helloDevOps, 0666, NULL);

6.4 APP如何使用

LinuxLiteos-a在APP层面都一样:

int main(int argc, char **argv)
{
	int fd;
	char buf[1024];
	int len;
	
	/* 1. 判断参数 */
	if (argc < 2) 
	{
		printf("Usage: %s -w <string>\n", argv[0]);
		printf("       %s -r\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open("/dev/hello", O_RDWR);
	if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return -1;
	}

	/* 3. 写文件或读文件 */
	if ((0 == strcmp(argv[1], "-w")) && (argc == 3))
	{
		len = strlen(argv[2]) + 1;
		len = len < 1024 ? len : 1024;
		write(fd, argv[2], len);
	}
	else
	{
		len = read(fd, buf, 1024);		
		buf[1023] = '\0';
		printf("APP read : %s\n", buf);
	}
	
	close(fd);
	
	return 0;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
将Ubuntu基于20.04版本移植到i.MX6ULL平台需要进行一系列步骤。首先,我们需要确保我们拥有i.MX6ULL开发板并正确连接上电源和显示器。然后,我们可以按照以下步骤进行移植: 1. 下载Ubuntu 20.04的ISO镜像文件,并将其烧录到一个可启动的USB设备中。 2. 在i.MX6ULL开发板上启动系统,并确保系统设置为从USB设备引导。 3. 进入系统安装界面,并按照提示进行基本设置,例如选择语言和键盘布局。 4. 在安装类型中,选择"其他选项"并进入分区工具。 5. 在分区工具中,创建一个适当的分区方案,包括根目录、/boot和swap等。 6. 选择要安装Ubuntu的目标分区,并进行扩展设置,如挂载点和格式化。确保选择的目标分区与i.MX6ULL平台兼容。 7. 点击继续安装并等待系统安装完成。 8. 完成安装后,重新启动系统并进入Ubuntu 20.04的登录界面。 9. 使用合适的凭据登录系统。 10. 安装所需的软件和驱动程序。在i.MX6ULL平台上,我们可能需要安装特定的驱动程序以确保设备正常工作。 11. 完成安装和配置后,我们现在可以开始使用Ubuntu 20.04在i.MX6ULL平台上开发和运行应用程序了。 总结来说,将Ubuntu基于20.04版本移植到i.MX6ULL平台需要确保正确安装系统镜像并进行适当的设置和配置。随后,我们可以根据需求安装软件和驱动程序,以满足在i.MX6ULL平台上的开发需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值