【ARM 裸机】C 语言 led 驱动

server/2024/9/18 12:38:00/ 标签: linux, arm开发, c语言

前面刚学习了汇编 led 驱动的编写和验证,现在开始就要进入 C 语言 led 驱动编写与验证了 !

1、C 语言运行环境构建

1.1、设置处理器模式

使 6ULL 处于 SVC 模式下,之前已经提到了处理器的九种模式,参考:【ARM 裸机】汇编 led 驱动之基本语法,如何设置成 SVC 模式,需要用到 CPSR 寄存器,CPSR 寄存器一共是 32 位,关注它的第 0~4 位,也就是设置为 M[4:0] 为 10011 = 0x13;
在这里插入图片描述
在这里插入图片描述
读写状态寄存器需要用到 MRS 和 MSR 指令,不能使用 LDR 和 STR 指令来对状态寄存器读写了;
在这里插入图片描述

1.2、设置 sp 指针

sp 指针可以指向内部 RAM,也可以指向 DDR,这里设置成指向 DDR,sp 设置到哪里?以正点原子开发板为例,512 MB 的范围是 0x80000000~0x9FFFFFFF,假定设置的栈的大小是 2 MB(0x20000),A7 的栈增长方式为向下增长,所以要设置成 sp 指针指向 0x80200000;

1.3、汇编跳转到 C 语言

使用 b 指令跳转到 C 语言的函数,比如 main 函数;

2、驱动编写

在 /home/zsw/linux/IMX6ULL/board_drivers 目录下新建一个 2_ledc 的目录,打开 VScode,在 VScode 中打开文件夹 2_ledc,然后将工作区另存为 ledc,然后新建文件 start.s、main.c、main.h、Makefile 这四个文件;
在这里插入图片描述
start.s

.global _start_start:// 设置处理器为 SVC 模式mrs r0, cpsr        // 读取 cpsr 到 r0bic r0, r0, #0x1f   // r0 & ~0x1f 清除 cpsr 的 bit4~0    bic  位清除指令orr r0, r0, #0x13   // r0 | 0x13 设置成 SVC 模式      orr 按位或msr cpsr, r0        // 把 r0 写入 cpsr// 设置 sp 指针ldr sp, =0x80200000// 跳转到 c 语言b main  

main.c

#include "main.h"// 使能外设时钟
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
void led_init(void)
{SW_MUX_GPIO1_IO03 = 0x5;   // 复用为 GPIO1_IO03SW_PAD_GPIO1_IO03 = 0x10b0;   // 配置电气属性// GPIO 初始化GPIO1_GDIR = 0x8;   // 设置为输出GPIO1_DR = 0x0;   // 打开 led
}
// 短延时
void delay_short(volatile unsigned int n)
{while(n--){}
}
// 长延时,在 396MHz 下一次循环大概 1 ms
void delay_ms(volatile unsigned int n)
{while(n--){delay_short(0x7ff);}
}   
// 打开 led
void led_on(void)
{GPIO1_DR &= ~(1<<3);   // bit3 清零
}
// 关闭 led
void led_off(void)
{GPIO1_DR |= (1<<3);   // bit3 置1
}
int main(void)
{clk_enable();led_init();// led 闪烁while (1){led_on();delay_ms(500);led_off();delay_ms(500);}return 0; 
}

main.h

#ifndef __MAIN_H
#define __MAIN_H// 定义要使用的寄存器// CCM相关寄存器地址
#define CCM_CCGR0           *((volatile unsigned int *)0x020c4068)
#define CCM_CCGR1           *((volatile unsigned int *)0x020c406c)
#define CCM_CCGR2           *((volatile unsigned int *)0x020c4070)
#define CCM_CCGR3  			*((volatile unsigned int *)0x020c4074)
#define CCM_CCGR4 			*((volatile unsigned int *)0x020c4078)
#define CCM_CCGR5 			*((volatile unsigned int *)0x020c407c)
#define CCM_CCGR6 			*((volatile unsigned int *)0x020c4080)// IOMUX相关寄存器地址 
#define SW_MUX_GPIO1_IO03 	*((volatile unsigned int *)0x020e0068)
#define SW_PAD_GPIO1_IO03 	*((volatile unsigned int *)0x020e02f4)// GPIO1相关寄存器地址 
#define GPIO1_DR 			*((volatile unsigned int *)0x0209c000)
#define GPIO1_GDIR 			*((volatile unsigned int *)0x0209c004)#endif

Makefile

objs = main.o start.oledc.bin: $(objs)arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o main.o -o ledc.elfarm-linux-gnueabihf-objcopy -O binary -S ledc.elf ledc.binarm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis%.o: %.carm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<%.o: %.sarm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<clean:rm -rf *.o ledc.bin ledc.elf ledc.dis

3、烧写验证

在这里插入图片描述

I.MX6ULL_MINI_ledc


演示视频中可以看到 led 每隔 500 ms 闪烁,到这里也就基本结束了本节的内容,但是还要补充一点: 链接脚本,在 Makefile 中,我们使用了下面这行代码,

	arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o main.o -o ledc.elf

上面的语句中我们是通过“-Ttext”来指定链接地址是 0X87800000 的,这样的话所有的文件都会链接到以 0X87800000 为起始地址的区域,看名字就知道链接脚本主要用于链接的,用于描述文件应该如何被链接在一起形成最终的可执行文件,其主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布,最简单的链接脚本可以只包含一个命“SECTIONS”,我们可以在这一个“SECTIONS”里面来描述输出文件的内存布局。我们一般编译出来的代码都包含在 text、data、bss 和 rodata 这四个段内;
下新建一个名为“imx6u.lds”的文件,然后在此文件里面输入如下所示代码,

SECTIONS{. = 0X87800000;.text :{start.o main.o *(.text)}.rodata ALIGN(4) : {*(.rodata*)} .data ALIGN(4) : { *(.data) } __bss_start = .; .bss ALIGN(4) : { *(.bss) *(COMMON) } __bss_end = .;
}

第 2 行设置定位计数器为 0X87800000,因为我们的链接地址就是0X87800000;第5行设置链接到开始位置的文件为start.o,因为 start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方;第 6 行是 main.o 这个文件,其实可以不用写出来,因为 main.o 的位置就无所谓了,可以由编译器自行决定链接位置;在第 11、13 行有“__bss_start”和“__bss_end”这两个东西?这个是什么呢?“__bss_start”和“__bss_end”是符号,第 11、13 这两行其实就是对这两个符号进行赋值,其值为定位符“.”,这两个符号用来保存.bss 段的起始地址和结束地址。前面说了.bss 段是定义了但是没有被初始化的变量,我们需要手动对.bss 段的变量清零的,因此我们需要知道.bss 段的起始和结束地址,这样我们直接对这段内存赋 0 即可完成清零。通过第 11、13 行代码,.bss 段的起始地址和结束地址就保存在了“__bss_start”和“__bss_end”中,我们就可以直接在汇编或者 C 文件里面使用这两个符号,修改 Makefile 中的这一行,

	arm-linux-gnueabihf-ld -Timx6u.lds -o ledc.elf

在这里插入图片描述
我们可以查看反汇编文件中的信息,链接脚本没啥问题;
在这里插入图片描述
现在重新编译工程,烧写验证结果是正确的,总结一下,本节从设置处理器模式开始,然后设置 sp 指针,跳转到 C 语言的 main,在 Makefile 中使用了变量,模式规则,自动化变量,最后还引入了链接脚本,东西比较多,需要时间去理解。


http://www.ppmy.cn/server/10313.html

相关文章

ui_admin_vue3启动

1、要求node版本16.20.2&#xff0c;小于这个版本npm run dev会报错UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token ‘??‘ 逻辑空赋值(??)是ES2021的语法&#xff0c;node v15.0.0以上才支持逻辑空赋值(??)的语法。之前为了兼容旧代码使用的node版本…

手拿滑块撕瑞数,我叫超弟你记住!腾讯滑块2024.4.23最新版,高复用,看到就是赚到,珍惜资源首次公开!!!

众所周知&#xff0c;腾讯阿里系安全属于国内天花板&#xff0c;让很多同行望眼欲穿&#xff0c;却束手无策&#xff0c;我们小团队因业务需要&#xff0c;一直都有现成的解决方案&#xff0c;有需求的朋友可以私信&#xff0c;后续准备办一个商学院&#xff0c;完全分享行内超…

【数据结构(邓俊辉)学习笔记】向量01——接口与实现

文章目录 0.意图1、概述2 从数组到向量3 向量ADT接口4 Vector 模板类5 构造与析构5.1默认构造方法5.2基于复制的构造方法5.3 析构方法 0.意图 一方面是将工作学习中零星的知识点串起来&#xff0c;另一方面向量是其他数据类型的基础&#xff0c;比如栈队列等&#xff0c;所以基…

Docker NetWork (网络)

Docker 为什么需要网络管理 容器的网络默认与宿主机及其他容器都是相互隔离的&#xff0c;但同时我们也要考虑下面的一些问题&#xff0c; 比如 多个容器之间是如何通信的容器和宿主机是如何通信的容器和外界主机是如何通信的容器中要运行一些网络应用(如 nginx、web 应用、数…

Gather:基于 DePIN 体系构建的 Web3 社交生态

“Gather 通过搭建一套基于 DePIN 的 Web3 社交体系&#xff0c;正在成为 SocialFi 革命的早期推动者。” 基于互联网的社交&#xff0c;正在为人们提供了远距离沟通和连接的便利&#xff0c;打破了地理障碍&#xff0c;这种具备包容性、广泛性的线上连接&#xff0c;加速了信息…

3D模型处理的并行化

今天我们将讨论如何使用 Python 多进程来处理大量3D数据。 我将讲述一些可能在手册中找到的一般信息&#xff0c;并分享我发现的一些小技巧&#xff0c;例如将 tqdm 与多处理 imap 结合使用以及并行处理存档。 那么我们为什么要诉诸并行计算呢&#xff1f; 使用数据有时会出现…

Linux文件/目录高级管理一 头歌

1 Linux文件权限修改 编程要求 根据右侧窗口命令行内的提示&#xff0c;在Begin - End区域内进行命令行语句补充&#xff0c;具体任务如下&#xff1a; 将系统已存在文件oldFile1的所有者权限设置为可执行&#xff1b; 为系统已存在文件oldFile2的同组用户权限删除可写权限&…

PyTorch如何保存验证集上效果最好的模型

PyTorch如何保存验证集上效果最好的模型 验证集的作用是在训练过程中监测是否出现过拟合。通常情况下&#xff0c;我们期望验证集的损失函数值在训练过程中首先下降&#xff0c;然后趋于稳定或上升。当损失函数值达到最小值时&#xff0c;表示模型在验证集上的泛化能力最佳&am…

VUE 弹框的实现

项目中常用且比较基础的功能 直接上代码&#xff1a; index.vue <template><div class"project-container"><!-- 新建 --><el-button type"normal" size"small" style"width: 120px" click"create"&…

Java复习第二十天学习笔记(过滤器Filter),附有道云笔记链接

【有道云笔记】二十 4.8 过滤器Filter https://note.youdao.com/s/dSofip3f 一、为什么要使用过滤器 项目开发中&#xff0c;经常会用到重复代码的实现。 1、请求每个servlet都要设置编码 2、判断用户是否登录&#xff0c;只有登录了才有操作权限。 二、过滤器相关Api int…

Android 开机流程

Android12 开机流程 Android开机流程的意义在于确保系统硬件设备的正确初始化、加载操作系统内核、启动系统服务,并最终使应用程序能够正常运行。它是Android系统启动的基础,保证了系统的可靠性和稳定性。 初始化硬件设备:在开机流程中,引导加载程序会初始化硬件设备,包括…

数据可视化(五):Pandas高级统计——函数映射、数据结构、分组聚合等问题解决,能否成为你的工作备用锦囊?

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

线性代数 --- 矩阵的对角化以及矩阵的n次幂

矩阵的对角化以及矩阵的n次幂 &#xff08;特征向量与特征值的应用&#xff09; 在上一篇文章中&#xff0c;我记录了我所学习的矩阵的特征向量和特征值&#xff0c;所关注的是那些矩阵A作用于向量x后&#xff0c;方向不发生改变的x(仅有尺度的缩放)。线性代数 --- 特征值与特…

【STL概念】

STL STL&#xff08;Standard Template Library),即标准模板库从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合。这里的“容器”和算法的集合指的是世界上很多聪明人很多年的杰作。STL的目的是标准化组件&#xff0…

linux 驱动-匹配1 (platform_bus_type)

目录 匹配入口 匹配顺序(platform_match ) 方式1: 方式2&#xff08;重点&#xff09; 方式3 方式4 (重点) 匹配入口 driver_match_device 实际调用platform_match driver_match_device(drv, dev);static inline int driver_match_device(struct device_driver *drv,str…

代码随想录算法训练营day1 | 704. 二分查找、27. 移除元素

数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。 因为数组的在内存空间的地址是连续的&#xff0c;所以我们在删除或者增添元素的时候&#xff0c;需要移动其他元素的地址。 那么二维数组在内存的空间地址是连续的么&#xff1f;不同编程语言的内存管理是不…

Centos7虚拟机与真机乎ping以及虚拟机ping不通的原因

虚拟机网络完全正常的标准 物理机可以ping通虚拟机的IP虚拟机可以ping通物理机的IP虚拟机可以ping通baidu.com等网站 使用工具版本&#xff1a;Centos7 前提&#xff1a; 虚拟机必须开机才可以连接访问 克隆出来的虚拟机一定要手动修改IP&#xff0c;IP冲突的情况下不能联网 …

Linux i2c-tool工具基础使用

一.i2cdetect i2cdetect 是一个用户空间程序&#xff0c;用于扫描 I2C 总线上的设备。它输出一个表格&#xff0c;其中包含指定总线上检测到的设备列表。以下是 i2cdetect 的使用方法&#xff1a; 运行扫描&#xff1a; 要执行 I2C 扫描&#xff0c;请使用以下命令&#xff1…

[C++][算法基础]求组合数(I)

给定 &#x1d45b; 组询问&#xff0c;每组询问给定两个整数 &#x1d44e;&#xff0c;&#x1d44f;&#xff0c;请你输出 的值。 输入格式 第一行包含整数 &#x1d45b;。 接下来 &#x1d45b; 行&#xff0c;每行包含一组 &#x1d44e; 和 &#x1d44f;。 输出格…

【STM32+HAL+Proteus】系列学习教程---串口USART(DMA 方式)定长,不定长收发。

实现目标 1、利用UART实现上位机PC与下位机开发板之间的数据通信 2、学会STM32CubeMX软件关于UART的DMA模式配置 3、具体目标&#xff1a;1、实现串口定长收发数据通信&#xff1b;2、串口不定长收发数据通信。 一、DMA简介 1、什么是DMA? DMA&#xff08;Direct Memory …