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

相关文章

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取