首页/新闻资讯/正文详情

Linux入门第二部分(ARM裸机篇) 第六章 驱动开发

发布时间:2026/11/5 9:14:05 来源:云帆数科 栏目:资讯中心
Linux入门第二部分(ARM裸机篇) 第六章 驱动开发
参考教程正点原子【第二期】手把手教你学Linux之ARMMX6U裸机篇_哔哩哔哩_bilibili一、I.MX6U寄存器定义与封装1、外设寄存器组结构体1需要说明的是驱动配置文件一般没有固定的写法但是会有相对比较高效的写法是值得参考的本章介绍的驱动配置文件写法主要参考STM32标准库。2一个外设基本上都会牵扯若干寄存器可以先将同属于一个外设的所有寄存器编写到一个结构体里面如下所示IOMUX_SW_MUX_Type就是IO复用寄存器组成员变量是每个IO对应的复用寄存器每个寄存器的地址是32位每个成员都使用“volatile”进行修饰目的是防止编译器优化。// IOMUX 寄存器组 typedef struct{ volatile unsigned int BOOT_MODE0; volatile unsigned int BOOT_MODE1; volatile unsigned int SNVS_TAMPER0; volatile unsigned int SNVS_TAMPER1; // ……… volatile unsigned int CSI_DATA00; volatile unsigned int CSI_DATA01; volatile unsigned int CSI_DATA02; volatile unsigned int CSI_DATA03; volatile unsigned int CSI_DATA04; volatile unsigned int CSI_DATA05; volatile unsigned int CSI_DATA06; volatile unsigned int CSI_DATA07; /* 为了缩短代码其余 IO 复用寄存器省略 */ }IOMUX_SW_MUX_Tpye;2、定义IO复用寄存器组的基地址1一个外设的寄存器组各寄存器的地址往往是连续的不一定全部都是连续的只是绝大部分可以将第一个寄存器的绝对地址作为基地址后续的寄存器只需要记相对于基地址的偏移地址即可这样只需要知道基地址和每个寄存器的偏移地址就能得到每个寄存器的绝对地址。2在定义外设寄存器组结构体时成员变量的摆放顺序也要与寄存器在存储空间中的排布顺序一致如果寄存地的排布不连续中间有空位那么定义结构体时也要定义占位预留的成员变量这样成员变量的偏移地址就是寄存器相对于寄存器组基地址的偏移地址。3根据上一小节中结构体IOMUX_SW_MUX_Type的定义其第一个成员变量为BOOT_MODE0也就是BOOT_MODE0这个IO的IO复用寄存器查阅I.MX6U的参考手册可以得知其地址为0x020E0014所以IO复用寄存器组的基地址就是0x020E0014定义如下#define IOMUX_SW_MUX_BASE (0x020E0014)3、定义寄存器组访问指针1如上小节所述寄存器相对于寄存器组基地址的偏移地址就是寄存器组结构体中成员变量的偏移地址也就是说实际物理存储空间中地址连续的寄存器存储结构与所定义的寄存器组结构体一致那么C代码就可以按照访问结构体成员的方式直接访问这些寄存器。2如果将实际物理存储空间中地址连续的寄存器抽象为所定义的寄存器组结构体那么寄存器组结构体的首成员地址就是寄存器组的基地址因此需要定义访问指针比如#define IOMUX_SW_MUX ((IOMUX_SW_MUX_Type *)IOMUX_SW_MUX_BASE)3定义访问指针后这个指针指向实际物理存储空间中的寄存器组结构体C代码可以通过该指针直接访问寄存器组的寄存器比如“IOMUX_SW_MUX-BOOT_MODE1”。二、程序编写与验证1、启动配置文件编写1在Board_Drivers文件夹中新建文件夹“3_ledc_plus”然后在该文件夹中新建start.s文件I.MX启动配置构建C语言运行环境的汇编代码将写在该文件中。2在start.s文件中写入如下内容基于上一章的start.s增加了清除bss段的操作.global _start .global _bss_start _bss_start: .word __bss_start 存放bss段起始地址__bss_start 在lds链接脚本定义 .global _bss_end _bss_end: .word __bss_end 存放bss段结束地址__bss_end 在lds链接脚本定义 _start 函数程序从此函数开始执行此函数主要功能是设置C运行环境 _start: 进入 SVC 模式 mrs r0, cpsr bic r0, r0, #0x1f 将r0的低5位清零也就是CPSR的M0~M4 orr r0, r0, #0x13 将r0或上0x13仅修改M[4:0],表示使用SVC模式 msr cpsr, r0 将r0的数据写入CPSR中 清除 bss 段 ldr r0, _bss_start r0 bss段起始地址 ldr r1, _bss_end r1 bss段结束地址 mov r2, #0 r2 清零填充值0 bss_loop: stmia r0!, {r2} 把r2的值写入r0指向内存写完r0自增4字节 cmp r0, r1 比较当前地址是否到达bss末尾该指令会操作CSPR的Z ble bss_loop 没到末尾就继续循环根据CSPR的Z判断CMP比较结果 ldr sp, 0X80200000 设置栈指针 b main 跳转到main函数2、外设配置文件编写1在文件夹3_ledc_plus中继续新建imx6ul.h文件外设配置的定义写在imx6ul.h中。2在imx6ul.h文件中写入如下内容①防止重复编译框架#ifndef __IMX6UL_H #define __IMX6UL_H #endif②外设寄存器组基地址定义// 外设寄存器组的基地址 #define CCM_BASE (0X020C4000) #define CCM_ANALOG_BASE (0X020C8000) #define IOMUX_SW_MUX_BASE (0X020E0044) #define IOMUX_SW_PAD_BASE (0X020E0204) #define GPIO1_BASE (0x0209C000) #define GPIO2_BASE (0x020A0000) #define GPIO3_BASE (0x020A4000) #define GPIO4_BASE (0x020A8000) #define GPIO5_BASE (0x020AC000)③CCM寄存器结构体定义typedef struct { volatile unsigned int CCR; volatile unsigned int CCDR; volatile unsigned int CSR; // ......省略中间寄存器需要自行补全否则可能无法正常工作 volatile unsigned int CCGR6; volatile unsigned int RESERVED_3[1]; // 预留占位 volatile unsigned int CMEOR; } CCM_Type; typedef struct { volatile unsigned int PLL_ARM; volatile unsigned int PLL_ARM_SET; volatile unsigned int PLL_ARM_CLR; volatile unsigned int PLL_ARM_TOG; // ......省略中间寄存器 volatile unsigned int MISC2; volatile unsigned int MISC2_SET; volatile unsigned int MISC2_CLR; volatile unsigned int MISC2_TOG; } CCM_ANALOG_Type;④IOMUX寄存器结构体定义typedef struct { volatile unsigned int JTAG_MOD; volatile unsigned int JTAG_TMS; volatile unsigned int JTAG_TDO; // ......省略中间寄存器需要自行补全否则可能无法正常工作 volatile unsigned int CSI_DATA04; volatile unsigned int CSI_DATA05; volatile unsigned int CSI_DATA06; volatile unsigned int CSI_DATA07; } IOMUX_SW_MUX_Type; typedef struct { volatile unsigned int DRAM_ADDR00; volatile unsigned int DRAM_ADDR01; // ......省略中间寄存器需要自行补全否则可能无法正常工作 volatile unsigned int GRP_DDRPKE; volatile unsigned int GRP_DDRMODE; volatile unsigned int GRP_DDR_TYPE; } IOMUX_SW_PAD_Type;⑤GPIO寄存器结构体定义typedef struct { volatile unsigned int DR; // GPIO数据寄存器 volatile unsigned int GDIR; // GPIO方向寄存器 volatile unsigned int PSR; // GPIO状态寄存器 volatile unsigned int ICR1; // 中断配置寄存器1 volatile unsigned int ICR2; // 中断配置寄存器2 volatile unsigned int IMR; // 中断屏蔽寄存器 volatile unsigned int ISR; // 中断状态寄存器 volatile unsigned int EDGE_SEL; // 边沿选择寄存器 } GPIO_Type;⑥寄存器组访问指针定义// 外设指针 #define CCM ((CCM_Type *)CCM_BASE) #define CCM_ANALOG ((CCM_ANALOG_Type *)CCM_ANALOG_BASE) #define IOMUX_SW_MUX ((IOMUX_SW_MUX_Type *)IOMUX_SW_MUX_BASE) #define IOMUX_SW_PAD ((IOMUX_SW_PAD_Type *)IOMUX_SW_PAD_BASE) #define GPIO1 ((GPIO_Type *)GPIO1_BASE) #define GPIO2 ((GPIO_Type *)GPIO2_BASE) #define GPIO3 ((GPIO_Type *)GPIO3_BASE) #define GPIO4 ((GPIO_Type *)GPIO4_BASE) #define GPIO5 ((GPIO_Type *)GPIO5_BASE)3、C语言点灯程序编写1在文件夹3_ledc_plus中继续新建main.c文件将点灯的业务代码和LED外设初始化的代码写在main.c文件中。2在main.c文件中写入如下内容①先将程序基本框架搭起来如下所示#include imx6ul.h int main(void) { return 0; }②添加外设时钟初始化函数如下所示/* * description : 使能 I.MX6U 所有外设时钟 * param : 无 * return : 无 */ void clk_enable(void){ CCM-CCGR0 0XFFFFFFFF; CCM-CCGR1 0XFFFFFFFF; CCM-CCGR2 0XFFFFFFFF; CCM-CCGR3 0XFFFFFFFF; CCM-CCGR4 0XFFFFFFFF; CCM-CCGR5 0XFFFFFFFF; CCM-CCGR6 0XFFFFFFFF; }③添加LED对应GPIO的初始化函数如下所示/* * description : 初始化 LED 对应的 GPIO * param : 无 * return : 无 */ void led_init(void){ /* 1、初始化 IO 复用, 复用为 GPIO1_IO03 */ IOMUX_SW_MUX-GPIO1_IO03 0X5; /* 2、配置 GPIO1_IO03 的 IO 属性 *bit 16:0 HYS 关闭 *bit [15:14]: 00 默认下拉 *bit [13]: 0 kepper 功能 *bit [12]: 1 pull/keeper 使能 *bit [11]: 0 关闭开路输出 *bit [7:6]: 10 速度 100Mhz *bit [5:3]: 110 R0/6 驱动能力 *bit [0]: 0 低转换率 */ IOMUX_SW_PAD-GPIO1_IO03 0X10B0; /* 3、初始化 GPIO, GPIO1_IO03 设置为输出 */ GPIO1-GDIR 0X0000008; /* 4、设置 GPIO1_IO03 输出低电平打开 LED0 */ GPIO1-DR ~(1 3); }④添加LED灯开启和关闭函数如下所示/* * description : 打开 LED 灯 * param : 无 * return : 无 */ void led_on(void){ // 将 GPIO1_DR 的 bit3 清零 GPIO1-DR ~(13); } /* * description : 关闭 LED 灯 * param : 无 * return : 无 */ void led_off(void){ // 将 GPIO1_DR 的 bit3 置 1 GPIO1-DR | (13); }⑤添加延时函数如下所示/* * description : 短时间延时函数 * param - n : 要延时循环次数(空操作循环次数模式延时) * return : 无 */ void delay_short(volatile unsigned int n){ while(n--){} } /* * description : 延时函数,在 396Mhz 的主频下延时时间大约为 1ms * param - n : 要延时的 ms 数 * return : 无 */ void delay(volatile unsigned int n){ while(n--) { delay_short(0x7ff); } }⑥在main函数中完成LED灯闪烁的业务逻辑如下所示int main(void) { clk_enable(); // 使能所有的时钟 led_init(); // 初始化 LED while(1) { led_off(); // 关闭 LED delay(500); // 延时大约 500ms led_on(); // 打开 LED delay(500); // 延时大约 500ms } return 0; }4、Makefile文件与链接脚本编写1由于本章例程的架构与上一章类似故可直接复用上一章的Makefile将其复制到本工程的根目录下即可。2除了Makefile以外链接脚本也可直接复用将上一章的imx6u.lds复制到本工程的根目录下即可。5、程序烧写验证1将imxdownload拷贝到工程的根目录下也就是和ledc.bin处于同一个文件夹下并赋予其可执行权限。2将SD卡接入虚拟机然后在当前目录运行命令“./imxdownload ledc.bin /dev/sdb”即可将固件刷入SD卡设备名需要核对与/dev下的设备名一致然后在开发板上验证即可。

相关推荐

synchronized 底层深度解析
synchronized 底层深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 9:14:05

MySQL安装流程
MySQL安装流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 9:13:04

MPC-HC 下载安装与最佳配置教程:免费开源、轻量无广告、支持 4K 硬解的 Windows 播放器
MPC-HC 下载安装与最佳配置教程:免费开源、轻量无广告、支持 4K 硬解的 Windows 播放器

MPC-HC 全称为 Media Player Classic Home Cinema,是一款免费开源的 Windows 媒体播放器。它界面简洁、启动速度快、资源占用低,同时内置了基于 FFmpeg/Libav 的解码器,能够播放 MP4、MKV、AVI、MOV、FLV、HEVC、H.264、VP9、AV1 等常见音视频… · 2026/11/5 9:13:04

链表节点交换算法与虚拟头节点应用
链表节点交换算法与虚拟头节点应用

1. 链表节点交换的核心逻辑 链表节点交换是数据结构操作中的经典问题,特别是两两交换相邻节点的场景在实际开发中经常遇到。这种操作看似简单,但涉及指针操作的精确控制,稍有不慎就会导致链表断裂或内存泄漏。 1.1 为什么需要虚拟头节点 虚… · 2026/11/5 9:21:10

AI环境监测:从传感器到云端的智能守护
AI环境监测:从传感器到云端的智能守护

1. 项目概述:当AI成为环境守护者 去年夏天,我在参与某沿海城市空气质量监测项目时,亲眼目睹了AI算法如何提前72小时预警到一次赤潮爆发。这套部署在近海浮标上的智能监测系统,通过分析海水温度、PH值和浮游生物密度等18项指标&… · 2026/11/5 9:21:10

STM32读SD卡与FATFS移植:从扇区读写到掉电恢复
STM32读SD卡与FATFS移植:从扇区读写到掉电恢复

简介:这是一套面向STM32嵌入式学习者的SD卡读取与FATFS文件系统实践工程,围绕SPI/SDIO接口访问、扇区读写驱动、FATFS移植配置以及文件打开、读取、写入与关闭操作展开,适合具备C语言与STM32基础、希望掌握嵌入式文件管理的开发者。压缩包共2… · 2026/11/5 9:21:10

STM32 SD卡FATFS文件系统移植与稳定读写实战
STM32 SD卡FATFS文件系统移植与稳定读写实战

简介:这是一份面向嵌入式初学者与工程实践者的STM32 SD卡读写FATFS文件系统学习包,聚焦在KEIL环境下完成SD卡初始化、扇区读写与文件系统挂载,并通过f_open、f_read、f_write等接口实现文件打开、读取、写入和关闭。压缩包共229个文件&#x… · 2026/11/5 9:21:10

关系数据库设计:关系模式与完整性约束实战解析
关系数据库设计:关系模式与完整性约束实战解析

1. 关系数据库的核心基石:关系模式与完整性约束 从事数据库开发十余年,我见过太多因为忽视数据完整性而导致业务逻辑混乱的案例。上周刚处理过一个电商平台的订单数据异常:由于缺少外键约束,用户删除后历史订单全部变成"幽灵… · 2026/11/5 9:21:10

Python自动化测试核心技能与实战指南
Python自动化测试核心技能与实战指南

1. Python自动化测试入门指南刚接触自动化测试时,我花了整整三个月才搞明白哪些是真正需要掌握的核心技能。现在回想起来,如果当时有人能系统地告诉我这些知识点,至少能节省一半的学习时间。这篇文章就是把我这些年积累的Python自动化测试基础… · 2026/11/5 9:20:10

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

多智能体协作系统架构设计与实践优化
多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39

大模型系统性入门实战指南:从原理到部署微调的全链路路线
大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39

鸿蒙文件系统沙箱机制解析与开发实践
鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码