LJ` Blog - Linux
type: channel
link: http://152.136.170.31/index.php/category/Linux/
items: Qt交叉编译开发流程
items: 瑞芯微SDK之修改自己的SDK配置文件
items: 瑞芯微SDK之buildroot配置文件系统
items: MIPI转LVDS芯片与屏幕驱动
items: Linux驱动应用-以misc为例
items: Linux驱动加载-以misc为例
items: Linux驱动编译-以misc为例
items: Linux驱动框架-以misc为例
items: DSMC驱动分析之RTOS
items: DSMC应用分析之DMA读写RTOS

Qt交叉编译开发流程
type: item
date: 2025-08-22T07:47:00+00:00
description: 1.为什么厂家buildroot编译出来的Qt不能通用与busybox不同，buildroot 不仅集成了 busybox，而且还集成了各种常见的第三方库和软件，需要什么就选择什么，buildroot 会帮我们处理好各种细节，根文件系统也会更加的合理、有效。但是厂家的buildroot编译脚本指定了编译路径，有些路径是根本不存在的，根本不会在该路径下生成Qt编译文件，很有可能厂家构建这一切用的虚拟机与提供给客户的不是同一个，如下图为Qt Creator搭建交叉编译环境时的提示。之后，我查看qmake文件的hex编码，发现有些路径根本就不存在，要么是路径下没有相关文件。尝试在该路径下复制文件也是无效的。2.为什么我们自己编译Qt很容易失败在linux下有些编译器，依赖之类，他会随着版本更新变化，然而有些过去依靠他们老版本制作的文件，比如旧版Qt源文件，在我们的环境更新后会出现某些语法问题、引用问题之类，这种问题让人无奈又头疼，比如从下面这些错误可见一斑，只能尝试一点点去解决这些问题。3.Qt开发具体流程3.1前提：确保交叉编译环境在命令行输入下面的命令验证是否有交叉编译环境，确认无误才可以编译。arm-linux-gnueabihf-gcc -v3.2 下载Qt官方源码进入下面的网址下载Qt源码，我们开发板用的是5.9版本，所以linux平台源码也要用5.9的，原则上应该可以下载低于5.9版本的，应该可以向下兼容，但是不能下载高版本的，为避免有问题，最好下载对应版本。https://download.qt.io/archive/qt/3.2编辑qtbase/mkspecs/qws/linux-arm-gnueabi-g++/qmake.conf，配置相关编译器及路径。这个配置cortex-a7是通用的，imx6ull也是一样的。3.3配置编译选项，即选择开启关闭哪些功能，此处是最容易出错的地方，配置有问题会导致后面的编译失败，有些问题只有作者才能解决。我这里通过的编译选项为：./configure -release -nomake examples -no-iconv -no-opengl -no-assimp -no-qt3d-profile-jobs -no-qt3d-profile-gl -xplatform linux-arm-gnueabi-g++ -prefix home/gzpeite/qt-everywhere-opensource-src-5.9.0/arm-qt -opensource -confirm-license也可以编写脚本，直接运行脚本即可，这里指定了我们自己的路径，肯定不会有问题。3.4 交叉编译生成Arm下的qmake执行完成上面的配置之后，就可以开始编译Qt，编译时间长且容易受到配置的影响导致失败，所以一般编译失败都可以看看是不是配置项哪里有问题。执行make命令 (可以多线程make -j4，代表4线程，具体看虚拟机和电脑配置)如果编译正常没有错误，此时可以make install，将其安装到我们在配置中指定的路径中去。3.4移植Qt到开发板文件系统（A40i可省略此步骤，因其可以在buildroot构建）将上面生成的arm-qt文件打包，通过U盘拷贝到文件系统中，比如usr/local/这个路径下面，然后配置Qt5的环境变量，编辑/etc/profile文件。export QT_ROOT=/usr/local/arm-qt export QT_QPA_PLATFORM_PLUGIN_PATH=$QT_ROOT/plugins export QT_QPA_PLATFORM=linuxfb:tty=/dev/fb0 export QT_PLUGIN_PATH=$QT_ROOT/plugins export LD_LIBRARY_PATH=$QT_ROOT/lib:$QT_ROOT/plugins/platforms export QML2_IMPORT_PATH=$QT_ROOT/qml至此，Qt编译完成，下面开始安装Qt Creator，编写程序测试环境。3.5安装Qt Creator下载 qt-opensource-linux-x64-5.9.0.run 这个文件。赋予该文件可执行权限，再执行安装。chmod u+x qt-opensource-linux-x64-5.9.0.run sudo ./qt-opensource-linux-x64-5.9.0.run 安装时需要用邮箱在Qt官网注册一下，按要求做即可。具体安装过程不在赘述，默认安装即可，linux又不分盘。3.6为Qt Creator创建Arm开发环境打开tools ->Optons配置qmake为之前编译的Arm下qmake…

瑞芯微SDK之修改自己的SDK配置文件
type: item
date: 2025-08-22T07:24:00+00:00
description: 1.更换SDK板级配置1.1 创建配置文件夹首先在SDK/device/rockchip/.chips/目录下创建我们自己的板级配置文件目录比如名字可以是ga3506，可以从官方的配置文件夹复制改名，这样不用自己写大部分的配置文件 1.2 创建配置文件然后就可以修改配置文件，比如可以先改名为ga3506_emmmc_rtt_defconfig，具体内容修改后面详细分析1.3 SDK更换板级配置然后可以使用命令./build.sh chip 选择芯片平台与配置文件2.具体文件修改如下图，可以看到复制过来是带有ok3506配置的，我们主要就是将红框内容修改为ga3506，同时在配置所用的目录下，对应复制粘贴修改名字为ga3506即可RK_BUILDROOT_BASE_CFG的配置文件目录为/home/linux/rk3506/OK3506_Linux_Source/buildroot/configs/ rockchip_ok3506_emmc_defconfig复制粘贴后改为rockchip_ga3506_emmc_defconfig RK_UBOOT_CFG的配置文件目录为 此SDK的UBOOT为不可改固件 RK_KERNEL_CFG的配置文件目录为 /home/linux/rk3506/OK3506_Linux_Source/kernel-6.1/arch/arm/configs/ OK3506-S_linux_rtt_defconfig复制粘贴后改为GA3506_linux_rtt_defconfig RK_KERNEL_DTS_NAME的配置文件目录为 /home/linux/rk3506/OK3506_Linux_Source/kernel-6.1/arch/arm/boot/dts/ OK3506-S_linux_emmc_rtt.dts复制粘贴改为GA3506_linux_emmc_rtt.dts OK3506-S_linux_emmc.dts复制粘贴改为GA3506_linux_emmc.dts ok3506-amp-rtt.dtsi复制粘贴改为GA3506-amp-rtt.dtsi 更改完成，我们就得到了一个GA3506的平台，然后就可以具体根据实际情况修改设备树与buildroot相关配置再使用./build.sh all命令全部编译一遍注意，如果有rtos，还需要将rtos的工程名字也做更改

瑞芯微SDK之buildroot配置文件系统
type: item
date: 2025-08-22T07:21:00+00:00
description: 1.更改buildroot在SDK根目录下使用./build.sh buildroot-config进入配置页面在buildroot目录下使用make menuconfig或直接改配置文件改完配置之后，保存配置到LinuxSDK/rk3576_linux6.1_release/buildroot/configs/rockchip_rk3xxx_defconfig1.1 增删文件系统软件，更改文件系统大小使用命令进入配置界面后，进入Target packages目标安装包，就是安装在文件系统的软件、库等文件。这里可以删除不用的软件包，比如chromium浏览器，测试文件等，也可以配置软件安装包，音频、压缩包、字体、通用软件比如QT、LVGL等，达到更改文件系统大小的目的1.2 个性化文件系统System configuration 主要修改系统配置，比如主机名字，密码等在此修改。如下图，可以在图形界面里配置系统设置，比如主机名，欢迎语，密码等1.3 注意事项下图是默认配置文件，不能带有空格，而make menuconfig生成的.config是带空格和注释的，所以没法直接替换保存用，那么我一般都是生成.config后对照更改配置名添加到这个默认配置中，从下面可以看出主机名字、密码、编译后覆盖文件夹、相机、音频、GPU等文件配置为什么要这样做，因为buildroot编译脚本从这个默认配置生成.config再去编译整个系统，直接中止编译改.config然后再编译是不行的，buildroot会提示配置文件改变，让你删除整个文件系统的编译重新来过注意如果使用buildroot默认构建的QT5.15，那么需要把Wayland也编译，因为buildroot系统的QT依赖此模块，如果想去掉可以使用自己编译的QT，此时可以去掉buildroot的QT与Wayland配置，使用linuxfb直接显示自己编译的QT即可，即在profile中增加export QT_QPA_PLATFORM=linuxfb2.向文件系统增加文件，修改默认文件2.1 在文件系统编译时添加修改在LinuxSDK/rk3576_linux6.1_release/buildroot/system/skeleton目录下，可以放在文件系统编译完成后想要打包进去的文件，比如自己编译的QT库文件，脚本，应用等，注意很多系统文件是带默认值的，即编译完成后覆盖，在此处添加或者修改的系统文件编译后会被覆盖原文件2.2 文件系统编译完成后添加修改在LinuxSDK/rk3576_linux6.1_release/buildroot/board/rockchip/rk3576/fs-overlay目录下，文件覆盖等级会更高，如果在skeleton更改后系统并未发生变化，是因为有些文件在脚本中配置，那么需要在fs-overlay目录中去创建此文件覆盖，因为其会在全部文件系统编译完成后覆盖一次，比如说etc/inittab的串口登录配置在脚本中生成，发生在skeleton之后，那么就得在fs-overlay中去添加文件覆盖

MIPI转LVDS芯片与屏幕驱动
type: item
date: 2025-08-22T07:16:00+00:00
description: 内核I2C驱动GM8775一. 屏幕参数计算屏幕型号：星科QV185FHM-N81.FY 18.5寸LVDS接口屏幕屏幕参数计算（详见LVDS 屏幕 M215HGE-L21 在 rk3288 上的适配过程 - 大窟窿 - 博客园） clock-frequency = <74000000>; hactive = <1920>; vactive = <1080>; hfront-porch = <100>; hsync-len = <5>; hback-porch = <35>; vfront-porch = <20>; vsync-len = <10>; vback-porch = <15>; hsync-active = <0>; vsync-active = <0>; de-active = <0>; pixelclk-active = <1>;我们需要的参数就是这些，在大部分屏幕手册中，会给出上面这些相关参数，也有很多屏幕是没有参数的，需要自己计算我们这个屏幕，手册中的参数是只有下图这些参数 从图片中可以知道已知参数频率为clock-frequency为74.25Mhz disp_timings 中的 hback-porch, hfront-porch, vback-porch, vfront-porch, hsync-len, vsync-len 的值。它们之间的关系如下： Tv = vback-porch + vfront-porch + vsync-len + Tvd Th = hback-porch + hfront-porch + hsync-len + Thd 则 vback-porch + vfront-porch + vsync-len = 1125 - 1080 = 45 hback-porch + hfront-porch + hsync-len = 1100 - 960 = 140 这里只需要保证这 3 个参数之和等于那个数就可以了，对具体的值没有特别的要求 故得到以下参数： hback-porch = <35>; hfront-porch = <100>; hsync-len = <5>; 与 vfront-porch = <20>; vsync-len = <10>; vback-porch = <15>; 二. 生成配置指令2.1 GM8775C：MIPI转双路LVDS芯片配置方法：该芯片有一款上位机生成初始化指令的软件，分为两种方式I2C指令与MIPI指令I2C指令：指的是使用I2C写入芯片配置参数来使得芯片将SOC的MIPI信号正确转换为LVDS信号，即上面生成的参数MIPI指令：指的是上电初始化时使用MIPI写入芯片配置参数来使得芯片将SOC的MIPI信号正确转换为LVDS信号，即上面生成的参数（目前MIPI写入配置方式存在问题，待供应商支持）软件获取参数使用方法：首先填入屏幕参数，根据上面求得的参数，配置视频格式中的参考时钟、行场等参数，最后创建寄存器表，若配置方式选择I2C，直接用USB转I2C连接器写入即可显示，注意此芯片所有配置掉电丢失，也就是说每次开机都要写入配置ID看GM8775C的手册，I2C_ADDR 管脚通过上下拉选择 I2C 器件地址，拉高为：0x5A，拉低为：0x58Link01相关的参数看屏幕手册，VESA与8bit参数可在屏幕手册中找到 `配置LINK EVEN / ODD奇偶场，如果屏幕显示两侧未连续，则需要交换EVEN / ODD` `0x27是mipi指令的密码解锁地址，0x00是I2C指令的密码解锁地址` 按照图片中的指示与上述描述，在软件上填好并生成寄存器指令表三. 内核与设备树3.1 编写内核代码下列代码其实就是i2c驱动示例代码，加上前面生成寄存器指令表所写的gm8775c_reg_defaults数组，通过i2c驱动形式，将数组配置写入芯片，用以配置MIPI转LVDS，在此板卡对于不同型号的屏幕，只需要改gm8775c_reg_defaults数组即可，其余不需要改动#include <linux/clk.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/of_gpio.h> #include <linux/proc_fs.h> #inc…

Linux驱动应用-以misc为例
type: item
date: 2025-08-22T06:51:00+00:00
description: 在前面，我们已经实现了一个驱动，下面就可以写一个程序来使用一下这个驱动1.概念回顾之前的介绍中，我们提到了一个驱动的全部使用生命周期设备结构体->实现file_operations的文件读、写、打开、关闭函数->给出模块入口出口函数内核或者用户手动加载驱动->进入模块入口，注册设备结构体，在/dev下新增设备接口用户的APP启动 -> 打开设备接口 -> 通过设备结构体的file_operations调用misc_open -> 向设备写入数据 -> 通过设备结构体的file_operations调用misc_write -> 读设备的数据 -> 通过设备结构体的file_operations调用misc_read -> 关闭设备 -> 进入模块出口，释放设备结构体，调用misc_release 2.流程分析在这里，我们就要实现用户启动后的动作，辅以查看log，上面的设备驱动调用分析是否正确首先程序我们先打开设备，驱动会打印misc open然后读取设备的数据，设备驱动里我们给了默认值abcde，所以我们会读取到abcde再向设备写入了12345，驱动会在控制台打印write:12345最后我们关闭了设备，设备会注销并打印misc release通过这个流程的实现，相信Linux驱动的大致框架会在脑海里有一个初步的概念编译程序：aarch64-linux-gnu-gcc APP.c -o app将app文件拷贝到开发板上执行即可APP.C #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> int main(int argc, char * argv[]){ int fd; char buf[64] = {0}; fd = open("/dev/MISC_TEST",O_RDWR); if(fd < 0){ perror("open error\n"); return fd; } read(fd, &buf, sizeof(buf)); printf("buf %s\n",buf); char buf1[64] = "12345"; write(fd, &buf1, strlen(buf1)); close(fd); return 0; }

Linux驱动加载-以misc为例
type: item
date: 2025-08-22T06:50:56+00:00
description: linux驱动加载一.insmod rmmod lsmod加载模块insmod misc_test.ko查看系统加载的模块 lsmod删除加载的模块 rmmod misc_test.ko二.modprobe加载模块 modprobe misc_test.ko此命令buildroot一般默认不带，可以自动处理多个模块之间的依赖三.静态编译进内核，系统启动自动加载可以使用 dmesg | -grep misc_test 来查看相关启动log

Linux驱动编译-以misc为例
type: item
date: 2025-08-22T06:50:21+00:00
description: 编译前置不管在哪里增加驱动代码都要注意做这三步1.创建驱动文件夹再写驱动代码，如mkdir misc_test2.写完驱动代码要在驱动代码同级目录增加Makefile和Kconfig3.在驱动文件夹上一级的Makefile和Kconfig增加驱动文件夹的编译配置做好这三步，才可以进行编译root:~/LinuxSDK/rk3576_linux6.1_release/kernel-6.1/drivers/misc $ mkdir misc_test && cd misc_test root:~/LinuxSDK/rk3576_linux6.1_release/kernel-6.1/drivers/misc/misc_test$ touch misc_test.c Makefile Kconfig 编辑完成后 在misc_test文件夹的上级目录，比如/LinuxSDK/rk3576_linux6.1_release/kernel-6.1/drivers/misc，对misc下的Makefile与Kconfig文件增加如下内容 /LinuxSDK/rk3576_linux6.1_release/kernel-6.1/drivers/misc/Makefile + obj-y += misc_test/ /LinuxSDK/rk3576_linux6.1_release/kernel-6.1/drivers/misc/Kconfig + source "drivers/misc/misc_test/Kconfig"Makefile文件 KERNEL_DIR=../../../ #KERNEL_DIR=/home/l/share/tspi_linux_sdk_repo_20240131/kernel ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- export ARCH CROSS_COMPILE PWD=$(shell pwd) obj-m += misc_test.o all: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules .PHONE:clean clean: $(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean Kconfig文件 config MISC_TEST tristate "MISC TEST" help misc_test 1.编译进内核./build.sh kernel-config 进入配置页面，找到MISC_TEST配置，将其选中为Y，保存配置后./build.sh编译即可2.编译为模块./build.sh kernel-config 进入配置页面，找到MISC_TEST配置，将其选中为M，保存配置后./build.sh编译即可2.手动编译在驱动文件夹下直接使用make命令执行Makefile，也可以得到驱动模块KO文件，或者找到$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules语句里面应该填的编译器，内核目录，驱动模块目录，对应填写直接执行也可编译

Linux驱动框架-以misc为例
type: item
date: 2025-08-22T06:49:35+00:00
description: 此文会根据misc测试驱动的代码架构来讲解简单的驱动如何编写。1.驱动框架与Linux一切皆文件思想1.1 驱动思想理解Linux系统通过文件抽象实现设备管理，所有硬件设备都通过文件接口进行访问。这种设计体现在设备节点以文件形式存在于/dev目录使用标准文件操作接口（open/close/read/write）驱动开发者通过实现文件操作函数与硬件交互比如说我们下面的misc测试驱动，就是一个典型的最小驱动，包含Linux最常用的读、写、打开、关闭函数核心就是实现设备结构体的file_operations的文件读、写、打开、关闭函数，给出模块入口出口函数这个框架就类似JAVA的CRUD，属于是同一类思想，只不过驱动操作的是硬件设备数据，JAVA操作的是软件数据库管理理解这个核心，写SPI、I2C，或者其他简单的设备驱动都是大致相同的，首先实现设备结构体，再实现file_operations的函数赋给设备结构体，最后是模块入口出口函数misc（杂项）设备是Linux内核提供的一种简化字符设备驱动开发的机制主设备号固定为10，次设备号由内核动态分配（MISC_DYNAMIC_MINOR）自动创建设备节点（需配合udev机制）相比传统字符设备注册（register_chrdev），简化了开发流程适用于功能简单的设备驱动开发1.2 驱动使用过程生命周期设备结构体->实现file_operations的文件读、写、打开、关闭函数->给出模块入口出口函数内核或者用户手动加载驱动->进入模块入口，注册设备结构体，在/dev下新增设备接口用户的APP启动 -> 打开设备接口 -> 通过设备结构体的file_operations调用misc_open -> 向设备写入数据 -> 通过设备结构体的file_operations调用misc_write -> 读设备的数据 -> 通过设备结构体的file_operations调用misc_read -> 关闭设备 -> 进入模块出口，释放设备结构体，调用misc_release 下列就是一个简单的驱动核心部分内核驱动函数 说明 misc_open 设备初始化/资源分配 misc_read 从设备获取数据 misc_write 向设备发送数据 misc_release 设备资源释放 // 文件操作结构体 struct file_operations misc_fops = { .owner = THIS_MODULE, .open = misc_open, .release = misc_release, .read = misc_read, .write = misc_write, }; // misc设备结构体 struct miscdevice misc_dev = { .minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号 .name = "hello_misc", // 设备节点名（/dev/hello_misc） .fops = &misc_fops, // 关联文件操作 }; module_init(misc_test_init); 模块加载入口函数 module_exit(misc_test_exit); 模块出口函数 1.3 驱动代码下面的程序我们用这种框架，实现一个简单的misc驱动，当用户加载或者释放驱动，都会有相应的提示，用户写会实现从用户空间拷贝数据，用户读会实现将拷贝来的数据拷贝到用户空间misc_test.c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/uaccess.h> //设备打开 int misc_open(struct inode *inode, struct file *file){ printk("misc open\n"); return 0; } //设备关闭 int misc_release(struct inode *inode, struct file *file){ printk("misc release\n"); return 0; } //读设备的数据 ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t){ char kbuf[64] = "qerwq"; if(copy_to_user(ubuf,kbuf,strlen(kbuf)) != 0){ printk(…

DSMC驱动分析之RTOS
type: item
date: 2025-08-22T06:47:00+00:00
description: 一 程序位置分布RTOS中DSMC代码，drv_dsmc_host.c是主机驱动代码，drv_dsmc_slave.c是从机驱动代码，dsmc_test.c是测试程序 SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_host.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/drv_dsmc_slave.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/tests/dsmc_test.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_host.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/hal/lib/hal/src/hal_dsmc_slave.c SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/dma.c 二 主机驱动代码解析drv_dsmc_host.c -> hal_dsmc_host.c -> drv_dsmc_host.c -> dsmc_test.c2.1 系统如何启动驱动？首先，在drv_dsmc_host.c主机驱动中rockchip_dsmc_host_probe(void)函数下方，使用了INIT_DEVICE_EXPORT(rockchip_dsmc_host_probe)将rockchip_dsmc_host_probe注册到了设备自动初始化函数这是RT-Thread自动初始化机制，听起来似乎很高大上，实际上只是将函数入口地址记录，在board_base.c中的rt_hw_board_init()启动板卡，完成基础初始化后，会在调用rt_components_board_init()，rt_components_board_init()内部实质上是执行函数指针，也就是执行对应的rockchip_dsmc_host_probe函数来启动驱动。2.2 主机驱动初始化代码流向rockchip_dsmc_host_probe -> HAL_DSMC_HOST_Init -> DSMC_HOST_DataInit -> DSMC_HOST_MapInit -> DSMC_HOST_LbInit -> DSMC_HOST_LbCmnConfig -> DSMC_HOST_LbCmnRgnConfig -> DSMC_HOST_LbCsrConfig -> lbReadReg与lbWriteReg在底层lbReadReg与lbWriteReg会去读写寄存器配置，然后逐级返回完成初始化打印HAL_DSMC_HOST_Init OK2.3 主机驱动代码框架drv_dsmc_host.c驱动主要做外设初始化与DMA数据拷贝搬运和直接读写指定地址的用户调用实现在RTOS中drv_dsmc_host.c的rockchip_dsmc_host_probe初始化函数会从RTOS层调用HAL层hal_dsmc_host.c文件的HAL_DSMC_HOST_Init去写寄存器初始化DSMC外设从下图drv_dsmc_host.c函数列表中可以看出来，除了初始化外设，主要就是DMA数据拷贝搬运的实现和直接读写指定地址的实现并且驱动通过ops操作集将DMA数据拷贝搬运和直接读写指定地址函数用作上层调用函数接口在drv_dsmc_host.c中，除了初始化函数，还对dsmc_host_ops这个ops操作集合做读写与底层拷贝等函数的实现，本质上类似 Linux 的 file_operations 或 platform_driver 的 ops，统一抽象了对硬件的访问接口（read/write/copy_from/copy_to等），便于上层调用和适配不同实现，（参考Linux通用的驱动篇）给ops赋值函数指针之后，在上层应用函数，就可以直接使用一个dsmc_host_ops类型的结构体，调用其读写函数，拿到DSMC通信数据struct dsmc_host_ops { rt_err_t (*read)(struct rockchip_rt_dsmc_host *dsmc_host, uint32_t cs, uint32_t region, unsigned long addr, uint32_t *data); rt_err_t (*write)(struct rockchip_rt_dsmc_host *dsmc_…

DSMC应用分析之DMA读写RTOS
type: item
date: 2025-08-22T06:47:00+00:00
description: 一 前言在前面分析了驱动大致走向和函数，驱动细节我们无法分析，因为拿不到底层寄存器相关的细节文档所以我们应用的重中之重，就是需要仔细分析DMA读写，把DMA读写学会，就可以拿到数据给上层应用使用DMA本身没有绑定DSMC，但是使用DMA驱动时，会给DMA配置DSMC的内存地址，这样DMA读写直接按块访问内存中映射的DSMC应用请直接看后面的 三 外部调用例程做库文件 ，例程修改为库文件和头文件作为接口库，提供对外接口调用二 RTOS DMA读写分析2.1 DMA驱动大致内容具体驱动分析请看前面的DSMC代码分析之RTOSdrv_dsmc_host.c驱动主要做外设初始化与DMA数据拷贝搬运和直接读写指定地址的用户调用实现在RTOS中drv_dsmc_host.c的rockchip_dsmc_host_probe初始化函数会从RTOS层调用HAL层hal_dsmc_host.c文件的HAL_DSMC_HOST_Init去写寄存器初始化DSMC外设从下图drv_dsmc_host.c函数列表中可以看出来，除了初始化外设，主要就是DMA数据拷贝搬运的实现和直接读写指定地址的实现并且驱动通过ops操作集将DMA数据拷贝搬运和直接读写指定地址函数用作上层调用函数接口2.2 DMA用户使用流程实际上是实现主控 → DSMC控制器 → Local Bus → FPGA等来做读写测试用户程序主要是通过DMA或直接地址读写向DSMC控制器写入读取数据，数据通过Local Bus总线，读写外部设备下面所有DMA相关函数copy_to、copy_from以及dma_memcpy函数，内部都通过rt_device_control对dma设置状态w完成DMA读写rt_device_control设置的参数最终在SDK/GA3506_Linux_Source/rtos/bsp/rockchip/common/drivers/dma.c文件中的rt_dma_control函数中通过switch来实现DMA具体操作 rt_device_control(dma, RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL, m2m_xfer); //请求 DMA 通道 rt_device_control(dma, RT_DEVICE_CTRL_DMA_SINGLE_PREPARE, m2m_xfer); //DMA 传输准备信号 rt_device_control(dma, RT_DEVICE_CTRL_DMA_START, m2m_xfer); //单次 DMA 传输准备 rt_device_control(dma, RT_DEVICE_CTRL_DMA_STOP, m2m_xfer); //停止 DMA rt_device_control(dma, RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL, m2m_xfer); //释放 DMA 通道程序结构大致就是下方这样的伪代码，从结构上我们就能看出来，主要是初始化后，通过ops操作集来读写内存拿到数据，或者通过DMA完成读写main() └──> dsmc_test() ├──> psram_simple_test() // PSRAM基本功能测试（CPU读写+数据校验） │ ├──> ops->write() // 向PSRAM写入测试数据 │ ├──> ops->read() // 从PSRAM读取测试数据 │ └──> 数据校验 ├──> dma_test_psram() // PSRAM DMA搬运测试（DMA搬运+数据校验） │ ├──> dma_memcpy() // 用DMA搬运数据到/从PSRAM │ └──> 数据校验 ├──> plc_simple_test() // PLC类外设基本功能测试（CPU读写+数据校验） │ ├──> ops->write() // 向PLC外设写入测试数据 │ ├──> ops->read() // 从PLC外设读取测试数据 │ ├──> ops->copy_to // 从DMA拷贝数据到FPGA │ ├──> ops->copy_to_state //拷贝状态 │ ├──> ops->copy_from// 从FPGA拷贝数据到DMA │ ├──> ops->copy_from_state//拷贝状态 │ └──> 数据校验 ├──> dma_test_lb() // Local Bus外设DMA搬运测试（DMA搬运+数据校验） │ ├──> dma_memcpy() // 用DMA搬运数据到/从Local Bus外设 │ └──> 数据校验 ├──> fpga_simple_test() // FPGA外设基本功能测试（C…