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

相关文章

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动