0%

1. Objects

1.1 父部件和子部件

子部件的位置是相对父部件的,父部件移动子部件跟着移动

1
2
3
4
5
lv_obj_t * parent = lv_obj_create(lv_scr_act());   /*Create a parent object on the current screen*/
lv_obj_set_size(parent, 100, 80); /*Set the size of the parent*/

lv_obj_t * obj1 = lv_obj_create(parent); /*Create an object on the previously created parent object*/
lv_obj_set_pos(obj1, 10, 10); /*Set the position of the new object*/

部件可以动态创建和删除,删除父部件会递归删除子部件:

阅读全文 »

1.什么是内存对齐?

以一个例子来说:

1
2
3
4
5
6
7
8
9
10
11
struct test{
char a;
int i;
char b;
};

int main(int argc, char **argv)
{
printf("size : %ld\n", sizeof(struct test));
return 0;
}

在 Inter i5 + Ubuntu 18.04 + gcc 7.5 上输出:

1
size : 12
阅读全文 »

1. 匿名管道

1
2
#include <unistd.h>
int pipe(int fd[2]);
  • 参数 fd 返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。fd[1]的输出是fd[0]的输入。
  • 返回值:若成功,返回0,若出错,返回-1

image-20240409184752559

  • 历史上是半双工的,现在某些系统提供全双工通道,不过为了移植性,最好还是假定管道是半双工的。
  • 管道只能在具有公共祖先的两个进程之间使用,进程调用fork之后,这个管道就能在父进程和子进程之间使用。
阅读全文 »

参考:

文章链接: https://subingwen.cn/cmake/CMake-primer/

文章作者: 苏丙榅

1.CMake

1.1 基本使用语法

1
2
3
cmake_minimum_required(VERSION 3.0)
project(CALC)
add_executable(app add.c div.c main.c mult.c sub.c)
  • cmake_minimum_required:指定使用的 cmake 的最低版本

    • 可选,非必须,如果不加可能会有警告
  • project:定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。

    1
    2
    3
    4
    5
    6
    7
    # PROJECT 指令的语法是:
    project(<PROJECT-NAME> [<language-name>...])
    project(<PROJECT-NAME>
    [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
    [DESCRIPTION <project-description-string>]
    [HOMEPAGE_URL <url-string>]
    [LANGUAGES <language-name>...])
  • add_executable:定义工程会生成一个可执行程序

    1
    add_executable(可执行程序名 源文件名称)
    • 源文件名可以是一个也可以是多个,如有多个可用空格或;间隔

      1
      2
      3
      4
      # 样式1
      add_executable(app add.c div.c main.c mult.c sub.c)
      # 样式2
      add_executable(app add.c;div.c;main.c;mult.c;sub.c)
阅读全文 »

1. 为什么要使用 mmap

应用程序和驱动程序之间传递数据时,可以通过 read、write 函数进行,如下图:

image-20240112114136049

应用程序不能直接读写驱动程序中的 buffer,需要在用户态 buffer 和内核态 buffer 之间进行一次数据拷贝。这种方式在数据量比较小时没什么问题; 但是数据量比较大时效率太低。例如更新 LCD,假设 LCD 采用 1024 × 600 × 32 bpp 的格式,则一帧数据就有 2.3 M左右,拷贝的步骤效率太低。

应该允许程序直接读写驱动程序中的 buffer,这可以通过 mmap 实现(memory map),把内核的 buffer 映射到用户态,让 APP 在用户态直接读写。

阅读全文 »

1. 休眠与唤醒

1.1 使用示例

使用休眠-唤醒机制来编写按键驱动程序:

  1. APP 调用 read 等函数试图读取数据,比如读取按键;
  2. APP 进入内核态,也就是调用驱动中的对应函数,发现有数据则复制到用户空间并马上返回;
  3. 如果 APP 在内核态,也就是在驱动程序中发现没有数据,则 APP 休眠,进程/线程状态改为非 Running;
  4. 当有数据时,比如当按下按键时,驱动程序的中断服务程序被调用,它会记录数据、将 APP 的进程/线程状态修改为 Running;
  5. 调度器调度 APP 继续运行它的内核态代码,也就是驱动程序中的函数,复制数据到用户空间并马上返回。

如下图所示:

阅读全文 »

1.异常和中断

1.1 异常和中断的概念

异常不可屏蔽,中断可以屏蔽,中断一种特殊的异常。

image-20240105105633741

  1. 初始化,设置中断源(设置中断源、设置中断控制器、使能CPU总开关)
  2. 执行程序
  3. 产生中断:中断源 -> 中断控制器 -> CPU
  4. CPU每执行完一条指令,都会检查有无中断/异常产生
  5. CPU发现有中断/异常产生,开始处理
  6. 对于不同的异常,跳去不同的地址执行指令(中断向量表),这些指令只是一条跳转指令,跳去执行某个函数
  7. 处理函数:保存现场,分辨中断源并调用相应的处理函数,恢复现场
阅读全文 »

1. GPIO 和 Pinctrl 子系统的使用

在 LED 驱动程序中,我们使用寄存器操作的方式来配置硬件,这种方式是非常低效的,对于具有大量寄存器的芯片肯定需要将寄存器操作,可以由芯片厂的BSP工程师来封装成库,然后来让驱动开发人员进行调用,这就是今天引入的 GPIO 和 Pinctrl 子系统。

1.1 Pinctrl 子系统

参考文档:内核目录\Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt

Pinctrl 子系统涉及 2 个对象:pin controller、client device。

阅读全文 »

1. 设备树

1.1 设备树的引入

如果按照上面的方式来让我们的Linux内核支持所有的主控和开发板;也许常用的主控芯片并不是很多,但是基于主控芯片所设计出的开发板就是那就是千千万万了。如果要将所有的开发板的引脚配置编译到内核中,那内核中将充斥大量的高度重复的文件。为此,引入了设备树的概念,设备树用来给内核里的驱动程序指定硬件的信息

image-20231218155959125

1.2 设备树的语法

阅读全文 »

1.LED驱动程序框架设计-分离

在上一篇博文中编写的驱动程序仅能操作固定开发板上的一个LED灯。考虑现实情况,使用同一款主控芯片,不同厂家可能会设计不同的PCB板,针对硬件使用的芯片引脚各不相同,但是同一款主控针对同一硬件的设置都是相似的。因此,我们考虑针对通用LED驱动程序,主控芯片,开发板来分离出三层,以便我们的LED驱动程序能够支持不同主控不同开发板使用:

image-20231202161816859

1.1 开发板层

led_resources.h

阅读全文 »