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

相关文章

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

OpenCV图像形态学的实现

《OpenCV图像形态学的实现》本文主要介绍了OpenCV图像形态学的实现,包括腐蚀、膨胀、开运算、闭运算、梯度运算、顶帽运算和黑帽运算,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起... 目录一、图像形态学简介二、腐蚀(Erosion)1. 原理2. OpenCV 实现三、膨胀China编程(

通过Spring层面进行事务回滚的实现

《通过Spring层面进行事务回滚的实现》本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下... 目录声明式事务回滚:1. 基础注解配置2. 指定回滚异常类型3. ​不回滚特殊场景编程式事务回滚:1. ​使用 TransactionT

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

SpringBatch数据写入实现

《SpringBatch数据写入实现》SpringBatch通过ItemWriter接口及其丰富的实现,提供了强大的数据写入能力,本文主要介绍了SpringBatch数据写入实现,具有一定的参考价值,... 目录python引言一、ItemWriter核心概念二、数据库写入实现三、文件写入实现四、多目标写入

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件