> 技术文档 > 正点原子-Linux学习-第二节-Linux之ARM(MX6U)裸机-学习笔记_正点核心板 以太网 传输速度最快

正点原子-Linux学习-第二节-Linux之ARM(MX6U)裸机-学习笔记_正点核心板 以太网 传输速度最快


一、开发环境搭建

文档教程:

​ 开发指南第四章

内容:

​ Ubuntu和Windows文件互传,Ubuntu安装FTP服务,Windows安装FTP软件filezilla作为客户端

​ Ubuntu下NFS和SSH服务的开启

​ Windows安装secureCRT或者mobaxterm或者Xshell或者Putty

二、交叉编译器的安装

内容:

​ Ubuntu安装交叉编译器,即GCC编译器,方便X86架构编译ARM架构能用的代码

​ 在资料盘的开发工具中发送交叉编译器工具到Ubuntu

​ 将文件传输到Ubuntu后,在指定目录创建文件夹:

sudo mkdir /usr/local/arm

​ 将复制到Ubuntu的文件再次复制到刚刚创建的文件夹:

sudo cp gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz /usr/local/arm/ -f

​ 将文件进行解压:

sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

​ 修改环境变量:

sudo vim /etc/profile

​ 在文件末尾添加如下内容:

export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin

​ 修改完成后,重启系统,安装相关库:

sudo apt-get install lsb-core lib32stdc++6

​ 输入以下命令查看交叉编译器版本号:

arm-linux-gnueabihf-gcc -v

三、VScode软件的安装与使用

​ 在Ubuntu 18.04系统上安装VS Code ≥ v1.86.2(测试到v1.87.1)时,会报出libc6libgssapi-krb5-2libxkbfile1 依赖库版本过低的问题,因此,需要使用历史版本的code:

​ 测试最新可以安装VS Code v1.85.2版本(2023年11月版本)。

​ 如何打开TongYi LingMa:

Ctrl+Shift+L

四、I.MX6U-ALPHA开发板简介

五、I.MX6U芯片简介

六、网络环境搭建

​ 开发板网线直连电脑网口,电脑WiFi上网,虚拟机桥接到上网网卡,此时三者设备处于同一网段,且此时虚拟机可以上网,但不能ping通开发板
有另一种方案:
正点原子-Linux学习-第二节-Linux之ARM(MX6U)裸机-学习笔记_正点核心板 以太网 传输速度最快
正点原子-Linux学习-第二节-Linux之ARM(MX6U)裸机-学习笔记_正点核心板 以太网 传输速度最快
虚拟网络编辑器:
正点原子-Linux学习-第二节-Linux之ARM(MX6U)裸机-学习笔记_正点核心板 以太网 传输速度最快
Ubuntu内网络设置:
正点原子-Linux学习-第二节-Linux之ARM(MX6U)裸机-学习笔记_正点核心板 以太网 传输速度最快
当虚拟机没有网络适配器图标时,或者修改完网络桥接、NAT设置后,输入以下命令:

sudo service network-manager stopsudo rm /var/lib/NetworkManager/NetworkManager.statesudo service network-manager start

此时,开发板能ping通电脑和虚拟机,并且电脑可以ping通虚拟机。
​ 当虚拟机没有网络时:

​ 设置当前虚拟机ip为静态ip,同时需要设置dns

文档教程:

​ I.MX6U常见问题汇总,搜静态,按照教程配置

开发板Uboot测试ping其他平台ip:

​ 此时三个设备设置为:

​ 开发板:

192.168.3.128

​ 电脑以太网接口:

192.168.3.200

​ Ubuntu:

192.168.3.127

​ 只能ping通同一个网口设备下的,桥接后可以ping不同网卡的,比如说在VM里没有桥接时,Ubuntu不能ping通开发板,因为开发板是连接到以太网接口的,但是Ubuntu是虚拟网卡,当虚拟网卡桥接到以太网接口时,再在Ubuntu中设置静态ip地址到和开发板同一个网段下,就可以ping通

​ 但是有一个问题,vscode的remote ssh不能连接到开发板或者是Ubuntu

七、汇编LED驱动实验

IO初始化

​ IO端口使能时钟

​ IO端口复用

​ IO端口电器属性配置

​ IO端口GPIO功能配置

汇编简介

​ 汇编是一条条指令构成的,也就是汇编指令

​ int a b;

​ a=b;

​ 区分于c语言操作内存的指令,汇编操作的寄存器,不能直接访问RAM(内存)中的数据

汇编驱动编写

​ 在Ubuntu端中的vscode新建s文件,进行汇编代码编写

​ 汇编指令可用大写,也可用小写

步骤

​ 1.使能全部外设时钟:CCGR0-CCGR6写入0xFFFFFFFF

​ 2.配置GPIO1_IO03 PIN复用为GPIO:配置寄存器的值

​ 3.配置GPIO1_IO03的电气属性:配置寄存器值,压摆率、驱动能力、速度、开漏输出、上下拉等

​ 4.设置GPIO1_IO03为输出:配置寄存器GDIR的bit3为1

​ 5.打开LED,设置GPIO_IO3为0:设置GPIO1_DR寄存器为0

编译程序

正点原子-Linux学习-第二节-Linux之ARM(MX6U)裸机-学习笔记_正点核心板 以太网 传输速度最快

​ 1.将c文件s文件变为o文件,使用arm-linux-gnueablihd-gcc

​ 2.将所有o文件链接为elf格式的可执行文件

​ 3.将elf文件转为bin文件

​ 4.将elf文件转为汇编,反汇编

​ 链接:

​ 链接就是将所有的o文件链接在一起,并且链接到指定的地方。本实验要求连接的时候要指定链接的起始地址,连接的起始地址就是程序运行的起始地址

​ 对于6ull来说,链接的起始地址应该指向RAM地址,RAM分为内部RAM和外部RAM,外部RAM就是DDR,也可以放到DDR中。本系列中裸机代码的链接起始地址为0x8780 0000。要使用DDR,就要初始化DDR,对于IMX来说,bin文件并不能直接运行,需要添加一个头部,这个头部信息包含了DDR的初始化参数,IMX系列的SOC内部boot rom会从SD卡,EMMC等外部存储中读取头部信息,然后初始化DDR,并且将bin文件拷贝到指定的地方

​ bin的运行地址一定要和链接的起始地址一致。

​ 后续代码编写完成后,使用makefile文件来集成编译;使用lds文件来链接文件,规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局

烧写代码-烧写bin文件

​ stm32烧写到内部FLASH

​ IMX支持SD卡,EMMC,NAND,nor,SPI flash等等启动。裸机例程选择烧写到SD卡中,

​ 在ubuntu中向SD卡中烧写裸机bin文件。烧写是指把bin文件烧写到SD卡中的绝对地址上。而且对于IMX而言,不能直接烧写bin文件,比如先在bin文件中添加头部,使用imxdownload软件

​ 使用imxdownload方法,确定SD卡所在文件

​ 赋予imxdownload权限,chmod 777 imxdownload

​ imxdownload会给bin文件添加一个头部,生成load.imx文件,并烧写到SD卡中

八、IMX启动方式

硬件启动方式选择:

​ LED灯实现,是从SD卡中读取bin文件并启动。IMX支持多种方式启动

​ IMX是如何支持多种外置flash启动程序

1.启动方式的选择

​ BOOT_MODE0和BOOT_MODE1,这两个是两个IO来控制的。选择从USB启动还是内部boot启动,如果要烧写系统到开发板中可以选择从USB下载,下载到SD卡中,EMMC、NAND等外置存储中,烧写完成设置从内部BOOT启动,然后从相应的外置存储中启动。

2.启动设备的选择

​ 前提是,设置MODE1和MODE0是从内部BOOT启动的,也就是MODE1=1,MODE0=0.

​ 支持哪些设备:

​ NOR flash,oneNAND、NAND flash,QSPI flash、EMMC、SD卡、EEPROM。常用的是NAND、SD、EMMC甚至QSPI FLASH

3.如何选择启动设备。

​ 通过BOOT_CFG选择,有BOOT_CFG1,2,4,每个8位。BOOT_CFG是由LCD_DATA0到23来设置的。在ALPHA开发板上,默认都接地。BOOT_CFG的8根线全部接地,除了BOOT_CFG2【3】,此位用来选择SD卡启动接口

​ 正点原子核心板上,LCD_DATA数据线基本下拉,自己做开发板的时候,选择4.7K上拉电阻即可调整BOOT_CFG。

启动头文件

1.boot rom做的事情

​ 设置内核时钟为398MHz,使能MMU和Cache,使能L1cache、L2cache、MMU,目的加速启动。

​ 从BOOT CFG设置的外置存储中,读取Image,然后做相应的处理

2.IVT和Boot Data数据

​ 在bin文件中添加头部信息。得到,烧写到SD卡中的load1.imx文件在SD卡中的起始地址是0x400地址

​ 头部大小为3KB,加上偏移的1KB,一共是4KB,因此在SD卡中的起始地址是4096

​ IVT大小为32B/4=8条

​ IVT+Boot DATA的数据,大多从NXP官方u-boot.imx文件中提取出来的

3.DCD数据

​ Device Configuration Data,DCD数据就是配置IMX内部寄存器配置的

​ 首先,将CCRG0到CCRG6全部写为0xFFFFFFFF,表示打开所有外设时钟。然后就是DDR初始化参数。设置DDR控制器,也就是初始化DDR

4.其他的数据

​ 检查数据命令、NOP命令、解锁命令。这些都属于DCD数据,DCD数据最大1768字节

九、C语言版LED驱动实验

C语言运行环境的构建

1.设置处理器模式

​ 设置6ULL处于SVC模式下,CPSR寄存器0-4位,设置10011=0x13.读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器里,MSR写入方向相反

2.设置sp指针

​ sp可以指向内部RAM,也可以指向DDR,这里将其指向DDR。SP指向哪里:512MB的范围0x80000000到0x9FFFFFFF。栈大小,0x200000=2MB。处理器栈增长方式,对于A7而 言,是向下增长的。设置sp指向0x80200000

3.跳转到C语言

​ 使用b指令,跳转到C语言函数,比如main函数。A系列程序开发需要自己写汇编函数,M系列比如STM32单片机,keil等软件会自动配置汇编指令

软件编写

链接脚本

编译下载

chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可。

./imxdownload key.bin /dev/sdd //烧写到 SD 卡中,不能烧写到/dev/sda 或 sda1 设备里面!

chmod 777 imxdownload./imxdownload key.bin /dev/sdd

💾 程序实际存放在 SD 卡的哪里?
imxdownload 工具会根据 i.MX6ULL 启动机制,将 clk.bin 烧写到 SD 卡的 特定偏移地址,而不是作为一个普通文件保存在 FAT 或 ext4 分区中。

✅ 存储位置(扇区偏移)
从 SD 卡的第 1KB(即 2 扇区,每扇区 512 字节)开始写入,也就是偏移地址 0x400。
也就是说,clk.bin 是被写入到 SD 卡的原始二进制区域,而不是某个文件系统内的路径下(如 /boot/ 或其他目录)。
这是为了配合 i.MX6ULL 的启动 ROM 从 SD 卡特定偏移读取启动代码(IVT + DCD + Image)。

📁 在 SD 卡上看不到这个文件的原因
因为你没有将 clk.bin 拷贝到 SD 卡的文件系统中,而是直接写入了 SD 卡的底层块设备(raw device)。
如果你在电脑上插入这张 SD 卡,使用文件管理器是 无法看到 clk.bin 文件 的,因为它不是以文件形式存在的。
你需要用工具如 dd 或十六进制编辑器(例如 hexdump、WinHex)查看 SD 卡的原始内容,才能看到写入的二进制代码。

十、模仿STM32驱动开发实验

STM32寄存器结构体详解

​ 对于STM32而言,使用一个结构体将所有的外设的所有寄存器都放到一起,将结构体抽象为各种外设寄存器,结构体成员相当于偏移地址(成员大小和寄存器大小对齐),也就是基地址加偏移地址

修改驱动

​ 1.添加清除bss段代码

​ 用来存放程序中未初始化的全局变量的内存区域,属于静态内存分配

​ 2.添加寄存器结构体

​ 在结构体中添加寄存器的时候要注意地址的连续性,如果不连续的话要添加占位。

​ 3.修改驱动

清除BSS段

十一、NXP官方SDK使用实验

新建cc.h文件

​ SDK包里面会用到很多数据类型,需要在cc.h文件定义很多常用的数据类型

移植文件

​ 需要移植的文件fsl_common.h、fsl_iomuxc.h、MCIMX6Y2.h

​ 设备为MICMX6Y2

十二、编写驱动

十三、IO函数

IOMUXC_SetPinMux();

IOMUXC_SetPinConfig();

十四、BSP工程管理实验

​ BSP工程管理的目的就是为了模块化整理代码,同一个属性的文件存放在同一个目录下面

​ 1.新建所需的文件夹,将统一属性的文件放到同一文件夹

​ mkdir obj

​ mkdir bsp

​ mkdir im6u

​ mkdir project

​ mkdir …

​ 2.修改clk、led、delay驱动,创建对应的驱动文件,然后放置到对应的目录中

​ 3.根据编写的新驱动文件,修改main.c文件内容

​ 设置VS code头文件路径。先创建.vscode目录,然后打开c/c++配置,然后会在该目录下生成一个脚c_pp_properties.json文件,在includePath中添加路径

​ ctrl+shift+p,输入c/c++:Edit… 然后进入

​ 创建.vscode文件夹、创建json文件、添加路径

十五、MakeFile编写

​ Makefile 是一种用于自动化构建程序的脚本文件,其核心原理是通过 make 工具解析 Makefile 中定义的规则,按照依赖关系依次执行命令,生成目标文件。

​ 路径变了,之前编写的MakeFile文件失效,需要重新编译一个通用的MakeFile文件

​ Makefile指定头文件路径,需要-I,编译源码的时候需要指定头文件路径。比如bsp/clk/bsp_clk.h变为-I bsp/clk/bsp_clk.h

​ 通过一堆的变量,将需要编译的原材料准备好了

​ Makefile静态模式

要快速理解这个 [Makefile](file:///home/wheeltec-client/linux/linux_study_board_driver/No_Os/07_key/Makefile) 文件,可以按照以下步骤进行速成:

1. 了解 Makefile 的基本结构

Makefile 是用于控制编译过程的脚本文件,定义了如何将源代码文件编译成可执行文件或目标文件。其核心内容包括:

  • 定义变量(如编译器、源文件列表等)
  • 设置规则(如何生成目标文件)

2. 分析关键变量

  • CROSS_COMPILE ?= arm-linux-gnueabihf-: 设置交叉编译工具链前缀。
  • TARGET ?= key: 最终生成的目标文件名。
  • CC, LD, OBJCOPY, OBJDUMP: 编译、链接和对象操作工具。
  • INCDIRS: 头文件路径列表。
  • SRCDIRS: 源文件路径列表。
  • INCLUDE := $(patsubst %, -I %, $(INCDIRS)): 将头文件路径转换为 -I 参数格式,供编译器使用。
  • SFILES, CFILES: 分别表示所有汇编文件(.S)和 C 源文件(.c)。
  • SOBJS, COBJS: 分别表示对应的 .o 目标文件。

3. 理解编译流程

  • $(TARGET).bin: 最终目标,生成 .bin 可执行文件。

    • 使用 ld 链接生成 .elf 文件。
    • 使用 objcopy 转换 .elf.bin
    • 使用 objdump 生成反汇编文件 .dis
  • $(SOBJS) : obj/%.o : %.S: 汇编文件编译规则。

  • $(COBJS) : obj/%.o : %.c: C 文件编译规则。


4. 理解清理规则

  • clean: 清理生成的文件(如 .elf, .dis, .bin, .o 等)。

5. 速记技巧

  • := 表示立即展开赋值。
  • ?= 表示如果变量未定义则赋值。
  • $^ 表示所有依赖文件。
  • $@ 表示目标文件。
  • $(wildcard ...): 获取匹配模式的文件列表。
  • $(patsubst ...): 模式替换函数,常用于生成目标文件路径。

6. 建议实践

  • 修改 TARGET: 更改目标文件名并重新编译,观察生成文件的变化。
  • 添加新源文件: 在 SRCDIRS 中添加新的源文件目录,测试是否能正确编译。
  • 尝试 make clean: 理解清理命令的作用。

通过以上步骤,你可以快速掌握该 Makefile 的作用,并具备修改和调试的能力。

十六、蜂鸣器实验

硬件原理图分析

​ S8550为PNP三极管,高位驱动,控制IO为SNVS_TAMPER1,低电平导通

硬件程序编写

​ 在bsp目录下创建beep文件夹,再创建bsp_beep.c和bsp_beep.h文件

​ 在c文件中:

​ 1.初始化SNVS_TEMPER1这个IO复用为GPIO

​ 2.设置SNVS_TEMPER1这个IO的电气属性

​ 3.初始化GPIO

​ 4.控制GPIO的输出高低电平

十七、按键输入实验

硬件原理图分析

​ 按键KEY0连接到了UART1_CTS引脚上,默认KEY0为高,当按下KEY0时,UART_CTS为低。

实验程序编写

​ 1.复用UART_CTS复用为GPIO1_IO18

​ 2.设置UART_CTS的电气属性

​ 3.设置UART_CTS为输入模式

​ 4.轮询UART_CTS引脚是否为高电平

驱动验证与调试

​ 加上清除BSS段,代码不运行

​ 对于32位SOC来说,一般是4字节访问的,bss段需要四字节对齐,芯片处理的时候,会从0x878000288开始清楚BSS段,然后这个地址不属于BSS段,所以需要对BSS START进行四字节对齐。0x8780028C这个地址才是起始对齐地址,所以需要设置BSS START进行四字节对齐

通用GPIO驱动编写与验证

​ 新建gpio文件夹,创建c、h文件

​ 创建gpio_init();gpio_pinwirte();gpio_pinread();函数

十八、主频和时钟配置实验

时钟树详解

原理图分析

​ 1.32.768KHz晶振,给RTC使用

​ 2.在6U的T16和T17这两个IO上接了一个24MHz的晶振

IMX系统时钟分析

​ 1.7路PLL

​ 为了方便生成时钟,6U从24MHz晶振生出来了7路PLL,这7路PLL中又生出来PFD

​ PLL1:ARM PLL供给ARM内核

​ PLL2:SYSTEM PLL,528MHz,528_PLL又分出记录PFD,分别是PLL2_PFD0-4

​ PLL3:USB1 PLL,480MHz,480_PLL,此路PLL分出来四路PFD,分别是PLL3_PFD0-4

​ PLL4:AUDIO PLL,主供给音频使用

​ PLL5:VIDEO PLL,主供给视频外设,比如RGB LCD,和图像处理有关的

​ PLL6:ENET PLL,主要供给网络外设

​ PLL7:USB_PLL,480MHz,无PFD

​ 2.各路PLL分出的PFD

​ 3.时钟树

​ 4.外设是如何选择合适的时钟的

​ 比如ESI时钟源选择:

​ PLL4、PLL3_PFD2、PLL5、PLL3

​ 5.要初始化的PLL和PFD

​ PLL1、PLL2、PLL2_PFD0-3、PLL3以及PLL3_PFD0-3

​ 一般按照时钟树来设置

主频修改

1.系统主频的配置

​ 要设置ARM主频为528MHz,设置CACRR寄存器的ARM_PODF为2分频,然后设置PLL1=1056MHz即可。CACRR的bit2-0为ARM_PODF位,可设置0-7,分别对应1-8分频。应该设置CACRR寄存器ARM_PODF=1

​ 设置PLL1=1056MHz,PLL1=PLL1_SW_CLK.PLL1_SW_CLK有两路可以选择,分别为PLL1_MAIN_CLK,和STEP_CLK,通过CCSR寄存器的PLL1_SW_CLK_SEL位(bit2)来选择,为0的时候选择PLL1_MAIN_CLK,为1的时候选STEP_CLK

​ 在修改PLL1的时候,也就是给系统时钟的时候,需要给IMX一个临时的时钟,也就是STEP_CLK,在修改PLL1的时候把PLL1_MAIN_CLK的时钟源切换到STEP_CLK上

​ 设置STEP_CLK,STEP_CLK也有两路来源,由CCSR的STEP_SEL位(bit8)来设置,为0的时候设置STEP_CLK为OSC=24MHz,为1的时候不重要,不用

​ 时钟切换成功以后就可以修改PLL1的值

​ 通过CCM_ANALOG_PLL_ARM寄存器的DIC_SELECT位(bit6-0)来设置PLL1的频率,公式为:OUTPUT=FREFxDIV_SEL/2 1056=24xDIV_SEL/2=>DIEV=88

​ 设置CCM_ANALOG_PLL_ARM寄存器的DIV_SELECT位=88即可。PLL=1056MHz

​ 还要设置CCM_ANALOG_PLL_ARM寄存器的ENABLE位(bit13)为1,也就是使能输出

​ 在切换回PLL1之前,设置CACRR寄存器的ARM_PODF=1,切记

​ 新建初始化时钟函数imx6u_clkinit();

​ 判断当前使用的哪个时钟源,如果是PLL1_MAIN_CLK,就要先设置STEP_CLK时钟

​ 设置PLL1=1056MHz

2.各个PLL时钟的配置

​ PLL2和PLL3。PLL2固定在528MHz,PLL3固定在480MHz

​ 1.初始化PLL2_PFD0-3。寄存器CMM_ANALOG_PFD_528用于设置四路PFD的时钟。比如PFD=528x18/PFD_FRAC。设置PFD0_FRAC位即可。比如PLL_PFD0=352MHz=528x18/PFD0_FRAC,因此PFD0_FRAC=27

​ 2.初始化PLL3_PFD0-3。寄存器

3.其他外设时钟源配置

​ AHB_CLK_ROOT、PERCLK_CLK_ROOT以及IPG_CLK_ROOT

​ 因为PERCLK_CLK_ROOT和IPG_CLK_ROOT要用到AHB_CLK_ROOT,所以我们要初始化AHB_CLK_ROOT

​ 1.AHB_CLK_ROOT的初始化

​ AHB_CLK_ROOT = 132MHz

​ 设置CBCMR寄存器的PER_PERIPH_CLK_SEL位,设置CBCDR寄存器的PERIPH_CLK_SEL位为0,设置CBCDR寄存器的AHB_PODF位为2,也就是3分频,396/3 = 132MHz,要等到CDHIPR位为0,等待握手信号完成。

​ PERCLK_CLK_ROOT = IPG_CLK_ROOT = 66MHz

​ 2.设置IPG_CLK_ROOT初始化

​ 设置CBCDR寄存器IPG_PODF=1,也就是2分频

​ 3.PERCLK_CLK_ROOT

​ 设置CSCMR1寄存器的PERCLK_CLK_SEL位为0,表示PERCLK的时钟源为IPG

8路PLL和8路PFD

其他时钟源设置

十九、GPIO中断实验

回顾STM32终端系统

1.STM32中断向量表

​ ARM芯片从0x00000000开始运行,执行指令。在程序开始的地方存放着中断向量表,中断向量表的主要功能是描述中断对应的中断服务函数

​ 对于STM32来说,代码最开始存放的地址存放栈顶指针,

2.中断向量偏移s

​ 一般ARM从0x00000000地址开始运行,对于STM32我们设置连接首地址为0x8000000

​ 如果代码一定要从0x8000000开始运行,那么要告诉一下SOC内核,也就是设置中断向量偏移

​ 设置SCB的VTOR寄存器为新的中断向量表起始地址即可

3.NVIC控制器

​ NVIC就是中断管理机构,使能和关闭指定的中断、设置中断优先级

4.中断服务函数的编写

​ 中断服务函数就是中断要做的事情

Cortex-A7中断系统

1.Cortex-A中断向量表

​ Cortex-A中断向量表有8个中断,其中重点关注IRQ。Cortex-A的中断向量表需要用户自己去定义

2.中断向量偏移

​ 裸机例程都是从0x87800000开始的,因此要设置中断向量偏移

3.GIC中断控制器

​ 同NVIC一样,GIC用于管理Cortex-A的中断。GIC提供了开关中断,设置中断优先级

4.IMX6U中断号

​ 为了区分不同的中断,引入了中断号 。ID0-ID15是给SGI,ID16-31是给PPI,剩下的ID32-1019是给SPI中断使用,也就是按键中断、串口中断

​ 6U支持128个中断

5.中断函数的编写

​ 一个是IRQ中断服务函数的编写,另一个就是在IRQ中断服务函数里面去查找并运行的具体外设的中断服务函数

GIC中断控制器

1.编写按键中断例程

​ KEY0使用UART1-CTS的这个IO,本节编写这个IO的中断

2.修改START.S

​ 添加中断向量表,编写中断服务函数和IRQ服务函数

编写服务中断服务函数,内容如下:

​ 1.关闭I、D Cache和MMU

​ 2.设置处理器9种工作模式下SP指针,要使用中断,那么必须设置IRQ模式下的SP指针,所以直接设置所有模式下SP指针

​ 3.清BSS段

​ 4.跳到C函数,也就是main函数

具体操作:

​ 添加复位中断服务函数向量表,有未定义指令中断服务函数、SVC中断服务函数、预取终止、数据终止、未使用、IRQ中断服务函数、FIQ中断,一共八个中断向量

​ 编写中断服务函数、未定义指令中断服务函数、SVC中断服务函数、预取终止中断服务函数、数据终止中断服务函数、未使用中断服务函数、IRQ中断服务函数、FIQ中断服务函数

3.CP15协处理器

​ MRC:将CP15协处理器中的寄存器数据督导ARM寄存器中

​ MRC就是读CP15寄存器,MCR就是写CP15寄存器,指令格式如下:

​ MCR{cond} p15,,,,

​ MRC p15,0,r0,c0,c0,0

​ 现在要关闭I、D Cache和MMU,打开Cortex-A7参考手册到105页,找到SCTLR寄存器,也就是系统控制寄存器,此寄存器bit0用于打开和关闭MMU,bit1对其控制位,bit2控制D Cache的打开和关闭,bit11用于预测分支的控制,bit12用于打开关闭I Cache

中断向量偏移设置

​ 将新的中断向量首地址写入到CP15协处理器的VBAR寄存器

IRQ中断服务函数

​ mrc p15,4,r1,c15,c0,0读取CP15的CBAR寄存器,(interface,接口)CBAR寄存器保存了GIC控制器的寄存器组首地址。GIC寄存器组偏移0x1000-0x1fff为GIC的分发器。0x2000-0x3fff为CPU接口端(CPU Interface)(参考A7参考手册),意味着我们可以访问GIC控制器

​ 代码中,R1寄存器保存着GIC寄存器的CPU接口端及地址。读取CPU接口端GICC-IAR寄存器的值保存到R0寄存器中。可以从GICC-IAR的bit9-0读取中断ID,读取中断ID的目的就是得到对应的中断处理函数

​ system-irqhandler就是具体的中断服务函数,可以给这个函数传递参数,就是GICC-IAR(中断ID)寄存器的值

​ 处理完system-irqhandler具体终端以后,需要将对应的中断ID值(GICC-IAR的值)写入到GICC-EOIR寄存器里

取址、译址、执行

6ULL-GPIO中断设置

​ 1.首先要设置GPIO的中断触发方式,也就是GPIO-ICR1和ICR2寄存器,触发模式有高电平触发、低电平触发,上升沿和下降沿。本次使用KEY0,也就是UART1-CTS,设置下降沿触发

​ 2.设置使能GPIO对应的中断GPIO-IMR寄存器

​ 3.处理完中断以后,需要清除中断标志位,也就是清除GPIO-ISR相应位,写1清零

​ GIC配置

​ 1.使能相应的中断ID,GPIO1-IO18对应的中断ID位

中断实验编写

​ 移植core-a7的h文件,进行int-init初始化、GIC-init初始化,中断初始化一定要放在代码最前面,避免找不到中断服务函数地址。

​ 新建bsp-exti的h文件c文件,初始化外部中断,void exit-init函数,需要包含bsp-gpio的h文件,设置复用io,设置输入模式,设置中断模式,初始化io,系统注册中断(irq-handler),中断中不允许使用延时,要求中断快进快出,中断处理完后要清除中断标志位

​ 中断初始化必须在所有程序之前,实验中将原本最开始的bss段放在了中断初始化的后面才可以正常进入中断

二十、EPIT定时器实验

EPIT简介

教程位置:IMX6ULL二十四章

1、32位定时器(0xFFFFFFFF),向下计数,到0后有两种工作模式,

2、时钟源可以选择ipg-clk=66mhz,

3、可以对时钟源进行分频,12bit分频0-4096分频

4、EPIT有两种工作模式,

​ set-and-forget模式,类似自动重装载模式,计时到比较值时可以输出引脚

​ free-and-running模式

5、IMX有两个EPIT定时器

​ EPIT-CR寄存器用于配置EPIT

实验原理简介

​ EPIT-CR设置bit0为1,bit1为1,设置计数器的初始值为加载寄存器的值,使能比较中断,bit3为1,设置定时器工作在set-and-forget模式下,bit15-bit4设置分频值,bit24-bit25设置时钟选择,设置为1,则EPIT的时钟源即为ipg-clk

​ EPIT-SR设置为bit0,表示有效,表示总段状态,写1清零,当OCIF位为1的时候表示有中断发生,为0的时候表示没有中断发生,因此在处理完中断定时器后要清除中断标志位

​ EPIT-LR寄存器设置定时器的加载值,定时器及时到0以后就会读取LR寄存器的值重新计数

​ CMPR比较计数器,当计数器的值和CMPR的值相同时,也会触发比较中断

​ 使用EPIT实现500ms周期的定时器,在中断服务函数中让LED闪灭

实验程序编写

​ 新建bsp-epit的c文件h文件,创建epit-init函数,判断frac的值,清零EPIT-CR寄存器,再设置为1,设置到bit1位,设置bit2为1,设置bit3为1,bit4为frac,bit24设置为1。EPIT-LR为加载寄存器,相当于倒计时,设置CMPR为0,初始化中断后,打开EPIT1-CR的bit0为1,打开定时器

​ 编写中断服务函数,epit-irqhandler,进入中断后设置状态标志位取反,然后判断EPIT-SR的bit0位是否有效,是否为1,有效则翻转LED灯,然后清除中断标志位,给EPIT-SR的bit0置0

二十一、定时器按键消抖实验

定时器按键消抖简介

当按键按下以后,进入到中断服务函数,开启一个定时器,定时周期为10ms,只有最后一个和抖动信号开启的定时器才可以完成一个定时周期,当周期结束后判断按键按下是否有效

实验原理简介

按键按下后进入按键中断处理函数,在按键中断处理函数中打开定时器计时,定时器计时到了之后触发定时器中断,进入定时器中断处理函数,在定时器中断处理函数里再次判断是否是按键按下。

实验程序编写

二十二、高精度延时实验

GPT定时器简介

以前的演示就是空循环,空指令,演示不准确,修改6ULL主频后,空指令演示函数不准了,因此需要使用高精度延时,并且延时不会随着主频变化而变化

STM32使用systick来实现高精度延时,这是硬件定时器,在6ULL也可以使用定时器来实现高精度延时

本实验使用到6ULL的GPT定时器来实现高精度延时

文档教程:IMX参考手册三十讲,开发指南20章

​ GPT为32位向上计数器,GPT提供两个外部引脚,可通过外部捕获来触发一些功能,并且支持EPIT的功能,比较输出、中断,同时也有一个12位分频器

​ GPT时钟源可以选择,这里选择ipg-clk=66mhz,作为时钟源

​ GPT定时器有两种工作模式,restart和free-run

​ Restart,模式下,定时器计数值和比较寄存器OCR的值相同的时候,会从新回到0开始向上计数,但只会在比较通道1时才可以

​ free-run,所有三个输出比较通道都可用,从0一直加到0xFFFFFFFF,然后周而复始

​ GPT-CR寄存器,bit0为使能位,为0关闭,为1有效。bit1确定GPT定时器计数器的初始值,为0标志GPT定时器从上次关闭的时候遗留的值,为1的时候从0开始。bit8-6设置时钟源,设置为1表示时钟源为ipg-clk=66mhz,bit9设置工作模式,为0工作在restart模式,bit15设置软件复位

​ GPT-PR寄存器的bit11-0为分频值,可设置0-4095,表示1-4096分频

​ GPT-SR状态寄存器,bit5表示溢出发生,bit4和bit3表示输入通道2和1的捕获中断标志位。bit2-0也就是OF3-OF1为比较中断

​ GPT-IR寄存器,也就是中断使能寄存器

GPT定时器使用

创建工作空间,修改之前的delay函数,

创建延时初始化函数,delay-init,

GPT-CR清零,CPT-CR的bit15置1,设置GPT-CR的bit1、bit6,设置GPT时钟源,为1pg-clk为66mhz,restart模式,默认计数寄存器为从0开始,分频设置,

设置GPT-PR,也就是bit0-12,设置65,也就是66分频,也就是1Mhz,然后是配置输出通道1,GPT-OCR【1】为1000000/2,设置中断周期为500ms,然后开中断

使能GIC

make -j12//打开12核编译

高精度演示原理简介

实验程序编写

二十三、串口实验

文档资料:6ULL参考手册55章、IMX6ULL参考指南第二十一章

串口协议简介

空闲位、起始位、数据位(8位、低位在前、高位在后传送)、停止位、波特率(一般115200)

UART一般有两种电平,一种时TTL一种时RS232,区别主要是电平不一样

6ULL串口UART原理

​ UART-URXD寄存器保存着串口接收到的数据,UART-UTXD寄存器为发送数据寄存器,如果需要串口发送数据只需要将数据存放到这个寄存器

UART-UCR1-UCR4都是串口控制寄存器,UART-UCR1的bit0是UART的使能位,为1的时候使能UART。bit14为自动检测波特率使能位,为1的时候使能波特率自动检测

UART-UCR2的bit0为软件复位位,为0的时候复位UART,bit1使能UART的接收,需要配置为1,bit2为使能发送,需要设置为1,bit5设置字长度,需要设置为1,0的话表示为7位。bit6设置停止位,1表示2位,0表示1位。bit7设置奇偶校验位,为0的时候是偶校验,为1的时候是寄校验。bit8是校验使能位,为0的时候是关闭校验位

UART-UCR3的bit2必须为1

​ UART-UCR4

UART-UFCR寄存器的bit7-9设置分频值,UART的时钟源,等于PLL3/6=80MHz,CSCDR1寄存器的UART-CLK-SEL设置为0的时候,UART时钟源为80MHz,为1的时候,UART的时钟源为24MHz晶振,CSCDR1寄存器的UART-CLK-PODF位控制分频,一般设置为1分频,因此UART_CLK_SEL=80MHz

UART_USR2寄存器的bit0为1的时候表示有数据可以读取。bit3为1的时候表示发送成功。

UART_UFCR、UART_UBIR、UART_UBMR决定了串口的波特率

实验程序编写

新建工作空间UART实验,新建UART子目录,新建bsp-uart的h文件c文件

​ 初始化UART1,固定波特率为115200。

​ UART的IO初始化。UART1-TX-DATA使用的IO为UART1-TXD

​ 设置电气属性

​ 初始化UART1,关闭串口,UCR1的bit0设置为0。打开串口,UCR1的bit0设置为1。UCR2的bit0为复位位,设置为0后等待

​ 配置串口数据位、奇偶校验、停止位

​ 配置串口波特率

​ 初始化UART

​ 通过UART发送一个字符,定义putc函数(发送时需要检查上一次的发送发送完成标志位,即USR2的bit3是否为1)、getc函数

​ 通过串口发送一串字符,定义puts函数

编译的时候报错,需要修改MakeFile中添加-fno-builtin

在Linux下一般是UTF-8编码,因此需要设置串口助手那边设置相同编码,否则会出现乱码,也有可能是波特率没设置对、时钟没设置对

移植printf函数

引入官方设置波特率函数自动设置波特率

IMX开发手册第二十二章

二十四、DDR实验

DDR内存简介

1、RAM和ROM

RAM:随机存储器,掉电数据丢失,操作数据不用指定地址

ROM:只读存储器,比如说FLASH、EMMC、UFS、NAND Flash操作数据需要指定数据地址

2、SRAM

静态随机存储器,单片机一般是芯片内部RAM,后期扩展需求,一般外部扩展SRAM,比如说IS62WV51216,一颗16位宽的1MB的SRAM。

3、SDRAM

同步动态随机存储器,SDRAM需要时钟线,一般是100MHz、133MHz、166MHz、200MHz。16bit=2Byte

4、DDR

DDR本质上是SDRAM,一个CLK传输两次数据

DDR3时间参数

1、传输速度

DDR3 1600、DDR4 2666这些单位位MT/s,每秒传输多少M次数据,

2、tRCD

3、CL参数

4、tRC参数

5、tRAS参数

IMX6U MMDC控制器

文档教程:IMX6ULL第三十五章

1、多模支持DDR3/DDR3L LPDDR2x16位

2、MMDC最高支持DDR3频率为400MHz,800MT/s

3、MMDC提供的DDR3连接信号,6ULL给DDR提供了专用的IO

DDR时钟配置

​ DDR使用的时钟源为MMDC_CLK_ROOT=PLL2_PFD2=396MHz,在前面的例程已经设置为396MHz。

​ CBCMR寄存器,的PRE_PERIPH2_CLK_SE位来选择,也就是bit22:21来设置,设置PRE_PERIPH2时钟源,设置为01,也就是PLL2-PFD2作为PRE_PERIPH2时钟源

​ CBCDR寄存器的PERIPH2-CLK_SEL位,也就是bit26,设置为0,PLL2作为MMDC时钟源,396MHz

​ CBCDR寄存器的FABRIC_MMDC_PODF位,bit5-3设置为0,也就是1分频最终MMDC_SLK_ROOT=396MHz

DDR3初始化、校准、超频测试

DDR_STRESS_TESTER,NXP官方DDR测试工具

如何使用:

​ 1.Excel配置文件

​ NXP使用的DDR为MT41K256M16HA-125;Memory type:DDR3-1600;DRAM density:4;DRAM Bus Width:16…具体参考NT5CCxMx_4Gb_DDR3(南亚)数据手册。Excel文件配置好后,realview.inc会同步更新

​ 2.inc文件

​ ddr_strees_tester工具新建一个inc文件,将Excel内的代码拷贝到inc文件

​ 3.测试

​ ddr_strees_tester通过USB将inc文件下载到开发板里,但如果直接进行压力测试会失败

​ 4.校准

​ ddr_strees_tester校准后(大概花费10多分钟),校准完成后,修改inc文件,然后就可以进行测试

二十五、RGBLCD实验

文档教程:6ULL参考手册第三十四章

1、像素点

类似点阵,每一个像素点就是一个小灯,RGBLCD的每一个像素点都由三种颜色(三原色)的小灯构成,通过调整三种颜色RGB颜色的比例,就可以显示不同的颜色

2、分辨率

要显示文字、图片、视频等,需要很多的像素点,分辨率就是指的像素点的格式,比如1080P、720P、2K、4K等等

1080P=1920x1080

2K=2560x1440

4k=3840x2160

尺寸指的是对角线长度

ppi=分辨率平方和开根号除以尺寸

3、像素格式

每种颜色用8bit来量化,RGB就需要888共24bit,也就是可以描述出2^24=1677万种颜色,如果是10bit,就是10亿+种颜色

在RGB888的基础上加上8bit的ALPHA通道,也就是透明通道,一共是RGB8888=32bit

4、LCD屏幕接口

RGB格式的屏幕,一般叫做RGB接口屏幕

屏幕接口有:MIPI、LVDS、RGB、MCU

正点原子屏幕接口ID:使用ID可以识别出不同的屏幕,在RGBLCD屏幕上R7、G7、B7焊接上下拉电阻实现不同的ID

bit0-bit31,分别是蓝、绿、红、ALPHA,一共32bit数据

SGM3157的作用(ALPHA底板RGB屏幕接口用了3个SMG3157):防止LCD屏幕上的ID电阻影响到6ULL系统启动

5、LCD时间参数和LCD时序

水平信号:HSYNC,水平同步信号,行同步信号,当出现这个信号的时候,表示新的一行开始显示

​ 1.产生HSYNC信号,表示新的一行开始显示,需要一定的持续时间,这个时间叫做HSPW

​ 2.HSYNC信号完成以后,需要一段时间延时,这段时间叫做HBP

​ 3.显示1024个像素点数据,需要1024个clk

​ 3.一行像素显示完成以后,到HSYNC下一个信号产生需要一个HFP时间后再进行新的一行显示,因此真正显示晚一行所需的时间计时:HSPW+HBP+WIDTH(水平像素点个数)+HFP

垂直信号:VSYNC,垂直同步信号,帧同步信号,当出现这个信号的时候,表示新的一帧开始显示

​ 1.VSYNC信号,持续一段时间,这段时间为VSPW

​ 2.VSPW时间完成后,需要一段时间延时,叫做VBP

​ 3.VBP信号结束以后,就是要显示的行数,比如600行

​ 4.所有行显示完成后,一段VFP延时,因此所有时间为(VSPW+VBP+height(600)+VFP)x(HSPW+HBP+WIDTH(水平像素点个数)+HFP)=一帧时间

6、显存

也就是显示存储空间,采用RGB8888=32bit=4B,这四字节数据表示一个像素点信息,必须存储起来,如果是1024x600x4=2.5MB,也就是一帧图像需要这么大的空间,因此需要留2.5MB空间给LCD用,方法就是定义一个数组u32 lcdfram【1024x600】

7、6ULL LCDIF控制器接口原理

​ 1.使用DOTCLK,也就是VSYNC、HSYNC、ENABLE、DOTCLK

​ 2.LCDIF_CTRL的bit1必须置0。bit1设置数据格式24位全部有效,设置为0。bit5设置LCD接口工作在主机模式下,必须要置1。bit9:8设置输入像素格式为24bit,置3。bit11:10,设置数据传输宽度为24bit,写3。bit13:12设置数据交换,不使用交换,设置为0。bit14:15,设置输入数据交换,不使用交换,设置为0。bit17置1,LCDIF工作在DOTCLK模式下。bit19必须置1,因为工作在DOTCLK模式下。bit31是复位功能,必须置0。

​ 3.LCDIF_CTRL1寄存器的bit19:16,设置0x7,设置24位的格式

​ 4.LCDIF_TRANSFER_COUNT寄存器的bit15:0是LCD一行的像素数,1024。bit31:16是LCD一共有多少行。

​ 5.LCDIF_VDCTRL0寄存器,bit17:0为VSPW参数。bit20设置VSYNC信宽带的单位,设置为1。bit21设置为1。bit24设置ENABLE信号极性,为0为低电平,为1是高电平。bit25设置时钟信号极性,设置为0。bit26设置SYNC信号极性,设置为0。bit27设置VSYNC信号机型,设置为0,低电平有效。bit28设置为1,开启ENBALE信号。bit29设置为0,VSYNC输出。

​ 6.LCDIF_VDCGR1寄存器为两个VSYNC信号之间的长度,那就是VSPW+VBPD+HEIGHT+VFP

​ 7.LCDIF_VDCGR2寄存器bit17:0是HSYNC信号之间的长度,那就是HSPW+HBP+HOZVAL+HFP。bit31:18为HSPW

​ 8.LCDIF_VDCGR3寄存器bit15:0是VBP+VSPW。bit27:16是HVP+HSPW

​ 9.LCDIF_VDCGR4寄存器bit17:0是一行有多少个像素点,一般是1024

​ 10.LCDIF_CUR_BUF,LCD当前缓存,现存首地址

​ 11.LCDIF_NEXT_BUF,LCD下一帧数据首地址,目的是加快显示速度。

​ 12.LCD IO初始化。

8、LCD像素时钟的设置

​ LCD需要一个CLK信号,这个时钟信号是6ULL的CLK引脚发送给RGB LCD的,比如7寸1024x600屏幕需要51.2MHz的CLK

​ LCDIF_CLK_ROOT就是6ULL的像素时钟,也就是PLL5

​ PLL5_CLK=Fref*DIV_SELECT=24x,DIV_SELECT就是CCM_ANALOG_PLL_VIDEO的bit6:0也就是DIV_SELECT位,可选范围27-54。设置PLL_VEDIO寄存器的bit20:19设置为2表示1分频。设置CCM_ANALOG_MISC2寄存器的bit31:30,也就是VIDEO_DIV为0,1分频。不使用小数分频器,也就是CCM_ANALOG_PLL_VIDE_NUM设置为0,再设置CCM_ANALOG_PLL_VIDEO_DENOM设置为0。设置CCM_CSCDR寄存器bit17:15设置LCDIF_PRE_CLK_SEL,选择LCDIF_CLK_ROOT的时钟源,设置为0x2,表示LCDIF的时钟源为PLL5,bit14:12为LCDIF_PRED位,也就是设置前级分频,可以设置0-7,分别对应1-8分频

​ CCM_CBCMR寄存器的bit25:23为LCDIF_PODF,设置第二级分频,可以设置0-7,分别对应1-8分频

​ 继续设置CCM_CSCDR2寄存器的bit11:9为LCDIF_CLK_SEL,选择LCD_CLK的最终时钟源,设置为0,LCDIF的最终时钟源来源于PRE_MUXED

9、LCD驱动程序编写

在这里插入图片描述

​ 创建c、h文件

LCDID读取、IO初始化

​ 1、初始化LCD之前,需要读取屏幕ID,区分不同的屏幕。

​ 2、打开模拟开关,设置LCD_VSYNC为高电平,复用VSYNC的引脚为GPIO输出功能

​ 3、读取屏幕ID,复用总线上(B7、G7、R7)的IO为GPIO,读取屏幕ID

​ 4、对比分析屏幕ID,返回对应的屏幕的宏定义

​ 5、测试读取ID程序,将读取到的ID打印出来

​ 6、初始化屏幕IO,先IO复用,设置电气属性,数据引脚、时钟、片选信号、背光信号等。

/*复位函数*/void lcd_reset(void){}/*停止复位*/void lcd_noreset(void){LCDIF->CTRL &= ~(1 << 31);}/*使能LCD控制器*/void lcd_enable(void){LCDIF->CGT |= (1 << 0);}

LCD时间参数、像素初始化

​ 1、复位、延时、停止复位。

​ 2、创建LCD屏幕信息结构体:屏幕宽度、高度、像素所占字节、屏幕显存起始地址、屏幕前景色、屏幕背景色、vspw、vbp、vfpd等

​ 3、根据不同的屏幕ID来设置屏幕参数,每个像素4个字节,设置显存起始地址(imux的起始地址0x8000 0000,显存地址从0x8900 0000),设置颜色的十六进制

​ 4、初始化像素时钟,传入参数:loopDiv、prediv、div

​ 第一个参数,设置DIV_SELECT,范围27-54

​ 第二个参数:设置为1-8

​ 第三个参数:可选范围1-8

​ LCD CLK = 24 * loopDiv / prediv / div

​ 初始化时钟的时候,不使用小数分频器

/*LCD时间参数、像素初始化*/lcdclk_init(unsigned char loopDiv,unsigned char prediv,unsigned div){ CM_ANALOG->PLL_VIDEO_NUM = 0; CCM_ANALOG->PLL_VIDEO_DENOM = 0; CCM_ANALOG->PLL_VIDEO = (1 << 13) | (2 << 19) | (loopDiv << 0) CCM_ANALOG->MISC2 &= ~(3 << 30); CCM->CSCDR2 &= ~(7 << 15); CCM->CSCDR2 |= (2 << 15); CCM->CSCDR2 &= ~(7 << 12); CCM->CSCDR2 |= (prediv - 1) << 12; CCM->CBCMR &= ~(7 << 23); CCM->CBCMR |= (div -1 ) << 23; CCM->CSCDR2 &= ~(7 << 9); CCM->CSCDR2 |= (0 << 9);}lcdclk_init(32,3,5);/*设置51.2MHz频率*/lcdclk_init(42,4,8);/*设置31.5MHz频率*/lcdclk_init(27,8,8);/*设置10.1MHz频率*/

LCDIF控制器接口初始化

/*LCDIF控制器接口初始化*/void lcd_init(){ LCDIF->CTRL = 0; LCDIF->CTRL |= (1 << 5) | (3 << 8) | (3 << 10) | (1 << 17) | (1 << 19); LCDIF->CTRL1 = 0; LCDIF->CTRL1 |= (7 << 16); LCDIF->TRANSFER_COUNT = 0; LCDIF->TRANSFER_COUNT |= (tdtlcd_dev.height << 16) | (tdtlcd_dev.WIDTH << 0); LCDIF->VDCTRL0 |= () LCDIF->VDCTRL0 |= (tftlcd_dev.VSPW << 0) | (1 << 20) | (1 << 21) | (1 << 24) | (0 << 25) | (0 << 26) | (0 << 27) | (1 << 28) | (0 << 29); LCDIF->VDCTRL1 = tftlcd_dev.vspw + tftlcd_dev.vbpd + tftlcd_dev.height + tftlcd_dev.vfpd; LCDIF->VDCTRL2 = (tftlcd_dev.HSPW + tftlcd_dev.hbpd + tftlcd_dev.width + tftlcd_dev.hfpd) | (tftlcd_devHSPW << 18); LCDIF->VDCTRL3 = (tftlcd_dev.VSPW + tftlcd_dev.vbpd) | ((tftlcd_dev.hSPW + tftlcd_dev.hbpd) << 16); LCDIF->VDCTRL4 = (tftlcd_dev.width) | (1 << 18); LCDIF->CUR_BUF = (unsigned int)tftlcd_dev.framebuffer; LCDIF->NEXT_BUF = (unsigned int)tftlcd_dev.framebuffer; lcd_enable(); delay_ms(20); lcd_clear(LCD_WHITE);}

10、LCD操作API编写

/*画点函数*/inline void lcd_drawpoint(unsigned short x, unsigned short y, unsigned int color);{*(unsigned int *)(unsigned int)(tftlcd_dev.framebuffer + tftlcd_dev.pixsize * (tftlcd_dev.width * y + x)) = color;}/*读点函数*/inline unsigned int lcd_readpoint(unsigned short x, unsigned short y);{ return *(unsigned int *)(unsigned int)(tftlcd_dev.framebuffer + tftlcd_dev.pixsize * (tftlcd_dev.width * y + x));}/*清屏函数*/void lcd_clear(unsigned int color){ unsigned int num; unsigned int i = 0; unsigned int *startaddr = (unsigned int*)tftlcd_dev.framebuffer; num = (unsigned int)tftlcd_dev.width * tftlcd_dev.height; for(i = 0;i < num;i++) { startaddr[i] = color; }}/*示例*/lcd_drawpoint(0, 0, LCD_RED);/*左上方*/lcd_drawpoint(tftlcd_dev.width - 1, 0, LCD_Red);/*右上方*/lcd_drawpoint(0,tftlcd_dev.height - 1, LCD_RED);/*左下方*/lcd_drawpoint(tftlcd_dev.width - 1, tftlcd_dev.height - 1, LCD_Red);/*右下方*/
#/*编译下载*/make -j12#/*清理工程*/make clean#/*下载代码*/./imxdownload lcd.bin /dev/sdf

​ 更多例程在

/开发板光盘/1.例程源码/1.裸机例程/16_lcd/bsp/lcd
/*示例*/tftlcd_dev.forcecolor = LCD_RED;tftlcd_dev.backcolor = LCD_WHITE;lcd_showstring(10, 10, 240, 32, 32, (char*)\"2025/4/10\");lcd_clear(LCD_RED);

二十六、RTC实验

6U内部RTC详解

#资料路径/I.MX6U开发板光盘/开发板光盘/7、I.MX6U参考资料/48章-SNVS

1、6U内部自带了一个RTC外设,确切的说是SRTC。6U和6ULL的RTC内容在48章SNVS章节。6U的RTC分为LP和HP。LP叫做SRTC,HP是RTC,但是,HP的RTC掉电以后数据丢失,即使用了纽扣电池也没用。所以必须使用LP,也就是SRTC。

​ SNVS章节有些是加密有关的,需要和NXP签订加密协议

​ RTC分为SNVS分为SNVS_LP和SNVS_HP。

​ 如果做产品,建议使用外置RTC芯片,建议PCF8563。

​ RTC很类似定时器,外接32.768KHz晶振,然后开始计时,RTC使用两个寄存器来保存计数值。

​ RTC的使用很简单,打开RTC就开始工作,我们需要做的就是不断读取RTC计数寄存器,读取时间值,或者写入RTC,也就是调整时间。

​ SNVS_HPCOMR的bit31置为1,表示所有的软件都可以访问SNVS所有寄存器。bit8也是和安全有关的,写0或者1都可以。

​ SNVS_LPCR寄存器,bit0置为1,使能SRTC功能。

​ SNVS_LPRTCMR是高15位RTC计数寄存器,每一秒加1.

​ SNVS_LPSRTCLR是低15位RTC计数器,与SNVS_LPRTCMR共同组成了SRTC计数器,每秒数据加1

​ 6U的RTC模式默认从1970年开始

2、

驱动编写与测试

1、新建c、h文件

​ 定义宏定义

/*h文件*/#ifndf __BSP_RTC_#define __BSP_RTC_#define SECONDS_IN_DAY (86400)#define SECONDS_IN_HOUR (3600)#define SECONDS_IN_MINUTE (60)#define DAYS_IN_AYEAR (365)#define YEAR_RANGE_STAR (1970)#define YEAR_RANGE_END (2099)/*跟时间有关的结构体*/struct rtc_datatime{ unsigned short year; unsigned charmonth; unsigned charday; unsigned charhour; unsigned charminute; unsigned charsecond;};void rtc_init(void);void rtc_enable(void);void rtc_disable(void);uint64_t rtc_converdate_to_seconds(struct rtc_datetime *datetime);void rtc_setdatetime(struct rtc_datetime *datetime);uint64_t rtc_getseconds(void);void rtc_convertseconds_to_datetime(unsigned int seconds,struct rtc_datetime *datetime);void rtc_getdatetime(struct rtc_datetime *datetime);unsigned char rtc_isleapyear(unsigned short year);#endif
/*c文件*/#include \"bsp_rtc.h\"/*rtc初始化函数*/void rtc_init(void){ struct rtc_datetime rtcDate; SNVS->HPCOMR |= (1 << 31) | (1 << 8); rtcDate.year = 2025; rtcDate.month = 4; rtcDate.day = 10; rtcDate.hour = 14; rtcDate.minute = 26; rtcDate.second = 0; /*开启RTC*/ rtc_enable(); }/*使能rtc*/void rtc_enable(void){ SNVS->LPCR |= 1 << 0; while((SNVS->LPCR & 0x01) == 0);}/*关闭rtc*/void rtc_disable(void){ SNVS->LPCR &= ~(1 << 0); while((SNVS->LPCR & 0x01) == 1);}/*将年月日转换成秒数*//*源码地址:/开发板光盘/1、例程源码/1、逻辑例程/17_rtc/bsp/rtc*/uint64_t rtc_converdate_to_seconds(struct rtc_datatime *datetime){ }/*时间设置函数*/void rtc_setdatetime(struct rtc_datetime *datetime){ unsigned int temp = SNVS->LPCR; rtc_disable(); seconds = rtc_coverdate_to_seconds(datetime); SNVS->LPSRTCMR = (unsigned int)(seconds >> 17);/*高15位*/ SNVS->LPSRTCLR = (unsigned int)(seconds << 15);/*低32位*/ if(temp & 0x01)rtc_enable();/*以前打开过rtc*/}/*读取秒数*/uint64_t rtc_getseconds(void){ uint64_t seconds = 0; seconds = ((uint64_t)((uint64_t)(SNCS->LPSRTCMR) << 17)) | (SNVS->LPSRTCLR >> 15); return seconds;}/*将秒转化为时间*/void rtc_convertseconds_to_datetime(unsigned int seconds,struct rtc_datetime *datetime){ }/*获取时间*/void rtc_getdatetime(struct rtc_datetime *datetime){ uint64 seconds = 0; seconds = rtc_getseconds(); rtc_converseconds_to_datetime(seconds, datetime); }/*判断闰年*/unsigned char rtc_isleapyear(unsigned short year){ }

时间错误问题调试

1、问题

当按照6U的参考手册编写代码,读取SRTC的LPSRTCMR和LPSRTCLR获取的时间是错误的,手册上写得LPSRTCMR是SRTC的高15位,LPSRTCLR是SRTC的低32位,也就是说RTC计数器是47位

2、问题解决方法

LPSRTCMR作为SRTC的高15位,但是LPSRTCLR的寄存器的bit31:15作为SRTC计数器的低17位。相当于SRTC的计数器是32位寄存器,不是47位。

参考NXP的相关资料:

/开发板光盘/7、I.MX6U参考资料/3、I.MX6ULL SDK包/device/MCIMX6Y2/drivers

二十七、I2C实验

I2C协议和6U的I2C控制器

1、ALPHA上有一个AP3216C,这是一个IIC接口环境光元器件

2、AP3216C连接到了I2C1上,SCL使用的是UART4_TXD,复用为ALT2,SDA使用的是UART_RXD,复用为ALT2

3、I2C分为SCL和SDA,两根线必须接4.7K电阻上拉3.3V

4、I2C总线支持多从机,通过从机地址来区分(7位地址位,1位读写位)

在这里插入图片描述
在这里插入图片描述

5、6U有4个IIC,标准模式100Kbit/s,快速模式

AP3216C简介与程序编写

I2C主机控制器驱动程序编写0

I2C主机控制器驱动程序编写1

I2C主机控制器驱动程序编写2

AP3216C程序编写

AP3216C程序调试以及bug解决

15位*/
SNVS->LPSRTCLR = (unsigned int)(seconds << 15);/低32位/

if(temp & 0x01)rtc_enable();/*以前打开过rtc*/

}
/读取秒数/
uint64_t rtc_getseconds(void){
uint64_t seconds = 0;

seconds = ((uint64_t)((uint64_t)(SNCS->LPSRTCMR) <LPSRTCLR >> 15);return seconds;

}
/将秒转化为时间/
void rtc_convertseconds_to_datetime(unsigned int seconds,struct rtc_datetime *datetime){

}
/获取时间/
void rtc_getdatetime(struct rtc_datetime *datetime){
uint64 seconds = 0;
seconds = rtc_getseconds();
rtc_converseconds_to_datetime(seconds, datetime);

}
/判断闰年/
unsigned char rtc_isleapyear(unsigned short year){

}

## 时间错误问题调试1、问题当按照6U的参考手册编写代码,读取SRTC的LPSRTCMR和LPSRTCLR获取的时间是错误的,手册上写得LPSRTCMR是SRTC的高15位,LPSRTCLR是SRTC的低32位,也就是说RTC计数器是47位2、问题解决方法LPSRTCMR作为SRTC的高15位,但是LPSRTCLR的寄存器的bit31:15作为SRTC计数器的低17位。**相当于SRTC的计数器是32位寄存器,不是47位。**参考NXP的相关资料:

/开发板光盘/7、I.MX6U参考资料/3、I.MX6ULL SDK包/device/MCIMX6Y2/drivers

# 二十七、I2C实验## I2C协议和6U的I2C控制器1、ALPHA上有一个AP3216C,这是一个IIC接口环境光元器件2、AP3216C连接到了I2C1上,SCL使用的是UART4_TXD,复用为ALT2,SDA使用的是UART_RXD,复用为ALT23、I2C分为SCL和SDA,两根线必须接4.7K电阻上拉3.3V4、I2C总线支持多从机,通过从机地址来区分(7位地址位,1位读写位)[外链图片转存中...(img-MijWUXS2-1752716298159)][外链图片转存中...(img-72LYB7sF-1752716298159)]5、6U有4个IIC,标准模式100Kbit/s,快速模式## AP3216C简介与程序编写## I2C主机控制器驱动程序编写0## I2C主机控制器驱动程序编写1## I2C主机控制器驱动程序编写2## AP3216C程序编写## AP3216C程序调试以及bug解决