i.MX6裸机开发(4):官方C库实现

2024-08-23 06:12
文章标签 实现 裸机 开发 官方 mx6

本文主要是介绍i.MX6裸机开发(4):官方C库实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在上两章我们分别使用汇编语言和C语言实现了点亮LED灯。 仔细分析代码不难发现我们仅仅操作了一个GPIO就需要自己查找、定义那么多寄存器。这样做的缺点很明显,易错、费时、代码可读性差。 NXP官方SDK中已经将所有的寄存器以及所有可用引脚的复用功能定义好了,本章将简单介绍这些内容并把它们添加到我们的程序。

本章主要内容:

  • 添加官方寄存器定义文件。

  • 添加官方引脚复用以及引脚属性定义文件。

  • 使用官方定义的寄存器、引脚设置函数实现RGB灯程序。

配套源码以及下载工具:

  • 路径:~/embed_linux_driver_tutorial_imx6_code/bare_metal/led_rgb_c

  • 野火裸机下载工具download_tool (路径:~/embed_linux_driver_tutorial_imx6_code/bare_metal/download-tool/download-tool.tar.bz2)。

1. 官方库文件介绍

本章移植的内容主要包括两部分。第一部分,移植官方寄存器定义文件。第二,移植引脚复用以及引脚属性定义文件。

1.1. 寄存器定义文件

在官方SDK的“ SDK_2.2_MCIM6ULL_EBF6ULL/devices/MCIMX6Y2 ”目录下, 头文件“ MCIMX6Y2.h ”文件多达4万多行,包含了i.MX6U芯片几乎所有的寄存器定义以及中断编号的定义, 本章我们只了解GPIO寄存器的定义,如下所示。

typedef struct {__IO uint32_t DR;     /**< GPIO data register, offset: 0x0 */__IO uint32_t GDIR;   /**< GPIO direction register, offset: 0x4 */__IO uint32_t PSR;    /**< GPIO pad status register, offset: 0x8 */__IO uint32_t ICR1;   /**< GPIO interrupt configuration register1,*/__IO uint32_t ICR2;   /**< GPIO interrupt configuration register2, */__IO uint32_t IMR;   /**< GPIO interrupt mask register, offset: 0x14 */__IO uint32_t ISR; /**< GPIO interrupt status register, offset: 0x18 */__IO uint32_t EDGE_SEL;/**< GPIO edge select register, offset: 0x1C */
} GPIO_Type;/*********************以下代码省略***************************8*/
/** Peripheral GPIO1 base address */
#define GPIO1_BASE                               (0x209C000u)
/** Peripheral GPIO1 base pointer */
#define GPIO1                                    ((GPIO_Type *)GPIO1_BASE)

这里只列GPIO1相关寄存器的部分代码。其他寄存器定义与此类似。 添加这些定义之后我们就可以直接使用“GPIO1->DR”语句操作GPIO1的DR寄存器。操作方法与STM32非常相似。

1.2. 引脚复用和引脚属性定义文件

使用每一个引脚之前我们都要选择引脚的复用功能以及引脚的pad属性。 在官方SDK中定义了所有可用引脚以及这些引脚的所有复用功能,我们需要哪种复用功能只需要选择即可, 并且官方SDK中提供了初始化函数。如下所示:

#define IOMUXC_GPIO1_IO00_I2C2_SCL \0x020E005CU, 0x0U, 0x020E05ACU, 0x1U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_GPT1_CAPTURE1L \0x020E005CU, 0x1U, 0x020E058CU, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_ANATOP_OTG1_IDL   \0x020E005CU, 0x2U, 0x020E04B8U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_ENET1_REF_CLK1L  \0x020E005CU, 0x3U, 0x020E0574U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_MQS_RIGHTL  \0x020E005CU, 0x4U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_GPIO1_IO00L  \0x020E005CU, 0x5U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_ENET1_1588_EVENT0_INL \0x020E005CU, 0x6U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_SRC_SYSTEM_RESETL  \0x020E005CU, 0x7U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO00_WDOG3_WDOG_BL   \0x020E005CU, 0x8U, 0x00000000U, 0x0U, 0x020E02E8U
#define IOMUXC_GPIO1_IO01_I2C2_SDAL    \0x020E0060U, 0x0U, 0x020E05B0U, 0x1U, 0x020E02ECU
#define IOMUXC_GPIO1_IO01_GPT1_COMPARE1L  \0x020E0060U, 0x1U, 0x00000000U, 0x0U, 0x020E02ECU
#define IOMUXC_GPIO1_IO01_USB_OTG1_OCL    \0x020E0060U, 0x2U, 0x020E0664U, 0x0U, 0x020E02ECUstatic inline void IOMUXC_SetPinMux(uint32_t muxRegister,uint32_t muxMode,uint32_t inputRegister,uint32_t inputDaisy,uint32_t configRegister,uint32_t inputOnfield)
{*((volatile uint32_t *)muxRegister) =IOMUXC_SW_MUX_CTL_PAD_MUX_MODE(muxMode) |\IOMUXC_SW_MUX_CTL_PAD_SION(inputOnfield);if (inputRegister){*((volatile uint32_t *)inputRegister) = \IOMUXC_SELECT_INPUT_DAISY(inputDaisy);}
}static inline void IOMUXC_SetPinConfig(uint32_t muxRegister,uint32_t muxMode,uint32_t inputRegister,uint32_t inputDaisy,uint32_t configRegister,uint32_t configValue)
{if (configRegister){*((volatile uint32_t *)configRegister) = configValue;}
}

这里只贴了一小部分代码,结合代码各部分说明如下:

  • 第1-24行,定义引脚的复用功能。这里只列出了“GPIO1_IO00”引脚的复用功能,其他引脚类似。 每个引脚对应多个宏定义代表引脚的不同的复用功能,以宏“IOMUXC_GPIO1_IO00_I2C2_SCL”为例, 它表示“GPIO1_IO00”引脚复用为“I2C2”的“SCL”引脚。这些宏定义将会用作某些函数的入口参数。

  • 第27-43行,引脚复用功能设置函数。函数“IOMUXC_SetPinMux”拥有6个入口参数, 但是前五个是通过第一部分的宏定义自动完成设置的。而第6个入口参数“inputOnfiled”用于设置是否开启读回引脚电平功能。

  • 第47-58行,引脚PAD属性设置函数。与第二部分相同,函数共有6个入口参数, 其中前五个是通过第一部分的宏定义自动完成设置的。而第6个参数用于设置PAD属性, 根据之前讲解每个引脚拥有一个32位PAD属性寄存器。第六个参数就是设置要填入PAD属性寄存器的值。 稍后我们将通过宏定义实现PAD属性设置。

  • “inline” 关键字在 C 和 C++ 编程中用于建议编译器将函数的代码直接嵌入到函数调用点,而不是通过通常的函数调用机制。这样可以:

    • 减少函数调用开销:避免压栈、跳转和返回以及参数传递等函数调用的开销,提高运行效率。

    • 增强代码优化:将函数内联后,编译器可以更容易进行一些优化操作,例如对内敛函数中的代码进行常量折叠、死代码消除等优化,这在非内联函数中可能是困难的。

    • 代码大小的平衡:对于非常大的函数,如果过多地使用‘inline’,可能会导致生成的二进制代码增大,因此使用时需要谨慎。

常见情况下使用inline的建议:

1. 函数体非常小,并且在程序中被频繁调用,使用“inline”可以避免函数调用的开销,从而提升性能。

2. 在性能要求较高的代码路径中,如果一个函数调用的开销影响了性能,可以使用“inline”来减小这些开销,如某些硬件访问函数可能需要尽量减少延迟。

3. 在C++中,模板函数常常被声明为“inline”。因为模板函数通常在头文件中定义,并且会在多个翻译单元中实例化,“inline”有助于避免重复定义的链接错误。

4. 对于递归函数,通常不适合使用“inline”,因为递归调用时编译器无法展开所有的调用。

2. 软件设计

2.1. 宏定义实现PAD属性设置

通常情况下一个引脚要设置8种PAD属性,而这些属性只能通过数字指定。 为简化PAD属性设置我们编写了一个PAD属性配置文件“pad_config.h”, 这里使用宏定义了引脚可选的PAD属性值,并且通过宏定义的名字很容易知道宏代表的属性值,如下所示。

引脚复用与PAD属性定义       (~/embed_linux_driver_tutorial_imx6_code/bare_metal/led_rgb_c/pad_config.h)

/* SPEED 带宽配置 */
#define SPEED_0_LOW_50MHz       IOMUXC_SW_PAD_CTL_PAD_SPEED(0)
#define SPEED_1_MEDIUM_100MHz   IOMUXC_SW_PAD_CTL_PAD_SPEED(1)
#define SPEED_2_MEDIUM_100MHz   IOMUXC_SW_PAD_CTL_PAD_SPEED(2)
#define SPEED_3_MAX_200MHz      IOMUXC_SW_PAD_CTL_PAD_SPEED(3)/* PUE 选择使用保持器还是上下拉 */
#define PUE_0_KEEPER_SELECTED       IOMUXC_SW_PAD_CTL_PAD_PUE(0)
#define PUE_1_PULL_SELECTED         IOMUXC_SW_PAD_CTL_PAD_PUE(1)/* PUS 上下拉配置 */
#define PUS_0_100K_OHM_PULL_DOWN  IOMUXC_SW_PAD_CTL_PAD_PUS(0)
#define PUS_1_47K_OHM_PULL_UP     IOMUXC_SW_PAD_CTL_PAD_PUS(1)
#define PUS_2_100K_OHM_PULL_UP    IOMUXC_SW_PAD_CTL_PAD_PUS(2)
#define PUS_3_22K_OHM_PULL_UP     IOMUXC_SW_PAD_CTL_PAD_PUS(3)

完整的代码请阅读源文件,这里只列出了文件“pad_config.h”部分代码,结合代码各部分简单说明如下:

  • 第1-5行,定义引脚带宽。从宏定义名可知带宽可设置为50M、100M、200M。

  • 第9-10行,定义引脚使用上下拉还是保持器。

  • 第14-17行,定义引脚的上下拉强度。当引脚设置为上下拉时,这些选项用于设置上下拉电阻大小。

2.2. RGB灯代码实现

与手动定义寄存器类似,这里使用官方SDK定义的寄存器并使用SDK提供的基本函数实现RGB灯功能,代码如下所示。

    RGB灯实现代码(~/embed_linux_driver_tutorial_imx6_code/bare_metal/led_rgb_c/led.c)

 /*************************第一部分************************/#include "MCIMX6Y2.h"#include "fsl_iomuxc.h"#include "pad_config.h"/*************************第二部分************************//*LED GPIO端口、引脚号及IOMUXC复用宏定义*/#define RGB_RED_LED_GPIO                GPIO1#define RGB_RED_LED_GPIO_PIN            (4U)#define RGB_RED_LED_IOMUXC              IOMUXC_GPIO1_IO04_GPIO1_IO04#define RGB_GREEN_LED_GPIO              GPIO4#define RGB_GREEN_LED_GPIO_PIN          (20U)#define RGB_GREEN_LED_IOMUXC            IOMUXC_CSI_HSYNC_GPIO4_IO20#define RGB_BLUE_LED_GPIO               GPIO4#define RGB_BLUE_LED_GPIO_PIN           (19U)#define RGB_BLUE_LED_IOMUXC             IOMUXC_CSI_VSYNC_GPIO4_IO19/*************************第三部分************************//* 所有引脚均使用同样的PAD配置 */#define LED_PAD_CONFIG_DATA            (SRE_0_SLOW_SLEW_RATE| \DSE_6_R0_6| \SPEED_2_MEDIUM_100MHz| \ODE_0_OPEN_DRAIN_DISABLED| \PKE_0_PULL_KEEPER_DISABLED| \PUE_0_KEEPER_SELECTED| \PUS_0_100K_OHM_PULL_DOWN| \HYS_0_HYSTERESIS_DISABLED)/* 配置说明 : *//* 转换速率: 转换速率慢驱动强度: R0/6带宽配置 : medium(100MHz)开漏配置: 关闭拉/保持器配置: 关闭拉/保持器选择: 保持器(上面已关闭,配置无效)上拉/下拉选择: 100K欧姆下拉(上面已关闭,配置无效)滞回器配置: 关闭 *//*************************第四部分************************//*简单延时函数*/void delay(uint32_t count){volatile uint32_t i = 0;for (i = 0; i < count; ++i){__asm("NOP"); /* 调用nop空指令 */}}int main(){/*************************第五部分************************/CCM->CCGR1 |= CCM_CCGR1_CG13(0x3);//开启GPIO1的时钟CCM->CCGR3 |= CCM_CCGR3_CG6(0x3); //开启GPIO4的时钟/*************************第六部分************************//*设置 红灯 引脚的复用功能以及PAD属性*/IOMUXC_SetPinMux(RGB_RED_LED_IOMUXC,0);IOMUXC_SetPinConfig(RGB_RED_LED_IOMUXC, LED_PAD_CONFIG_DATA);/*设置 绿灯 引脚的复用功能以及PAD属性*/IOMUXC_SetPinMux(RGB_GREEN_LED_IOMUXC,0);IOMUXC_SetPinConfig(RGB_GREEN_LED_IOMUXC, LED_PAD_CONFIG_DATA);/*设置 蓝灯 引脚的复用功能以及PAD属性*/IOMUXC_SetPinMux(RGB_BLUE_LED_IOMUXC,0);IOMUXC_SetPinConfig(RGB_BLUE_LED_IOMUXC, LED_PAD_CONFIG_DATA);/*************************第七部分************************/GPIO1->GDIR |= (1<<4);  //设置GPIO1_04为输出模式GPIO1->DR |= (1<<4);    //设置GPIO1_04输出电平为高电平GPIO4->GDIR |= (1<<20);  //设置GPIO4_20为输出模式GPIO4->DR |= (1<<20);    //设置GPIO4_20输出电平为高电平GPIO4->GDIR |= (1<<19);  //设置GPIO4_19为输出模式GPIO4->DR |= (1<<19);    //设置GPIO4_19输出电平为高电平/*************************第八部分************************/while(1){GPIO1->DR &= ~(1<<4); //红灯亮delay(0xFFFFF);GPIO1->DR |= (1<<4); //红灯灭GPIO4->DR &= ~(1<<20); //绿灯亮delay(0xFFFFF);GPIO4->DR |= (1<<20); //绿灯灭GPIO4->DR &= ~(1<<19); //蓝灯亮delay(0xFFFFF);GPIO4->DR |= (1<<19); //蓝灯灭}return 0;}

代码很容易理解,这里只做简单的说明。

  • 第一部分,添加头文件,文件“MCIMX6Y2.h”和“fsl_iomuxc.h”来自SDK。 文件“pad_config.h”是自己编写的文件,在其他工程中可直接使用。

  • 第二部分,定义LED灯相关引脚以及复用功能。

  • 第三部分,定义引脚的PAD属性。PAD属性宏定义保存在“pad_config.h”文件中, 这里使用“|”运算符将所有属性设置“合并”在一起,后面将作为函数参数。

  • 第四部分,简单的延时函数。

  • 第五部分,开启GPIO时钟。

  • 第六部分,设置引脚的复用功能以及引脚PAD属性。

  • 第七部分,设置GPIO为输出并设置初始电平为高电平。

  • 第八部分,在while(1)中依次点亮红灯、绿灯和蓝灯。

2.3. 编译下载

打开本章配套例程,在 文件夹下执行make命令,makefile工具便会自动完成程序的编译、链接、格式转换等工作。 正常情况下我们可以在当前目录看到生成的一些中间文件以及我们期待的.bin文件。

在编译下载官方SDK程序到开发板章节我们详细讲解了如何将二进制文件烧写到SD卡(烧写工具自动实现为二进制文件添加头)。 这里再次说明下载步骤。

  • 将一张空SD卡(烧写一定会破坏SD卡中原有数据!!!烧写前请保存好SD卡中的数据), 接入电脑后在虚拟机的右下角状态栏找到对应的SD卡。将其链接到虚拟机。

  • 进入烧写工具目录,执行 ./mkimage.sh <烧写文件路径> 命令,例如要烧写的led.bin位于home目录下, 则烧写命令为 ./mkimage.sh /home/led.bin 。

  • 执行上一步后会列出linux下可烧写的磁盘,选择你插入的SD卡即可。这一步非常危险!!! 一定要确定选择的是你插入的SD卡!!,如果选错很可能破坏你电脑磁盘内容,造成数据损坏!!! 确定磁盘后SD卡以“sd”开头,选择“sd”后面的字符即可。例如要烧写的sd卡是“sdb”则输入“b”即可。

3. 实验现象

将开发板设置为SD卡启动,接入SD卡,开发板上电,可以看到开发板上RGB红、绿、蓝三种颜色轮流闪烁。

这篇关于i.MX6裸机开发(4):官方C库实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1098576

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo