裸机就是手动的操作硬件来实现驱动设备,后面会有驱动框架不需要这么麻烦
第八章 汇编 LED 灯实验
核心过程
通过汇编语言来控制硬件(驱动程序)
代码流程
1、使能 GPIO1 时钟
GPIO1 的时钟由 CCM_CCGR1 的 bit27 和 bit26 这两个位控制,将这两个位都设置位 11 即 可。本教程所有例程已经将 I.MX6U 的所有外设时钟都已经打开了,因此这一步可以不用做。
2、设置 GPIO1_IO03 的复用功能
找到 GPIO1_IO03 的复用寄存器“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03”的地址为 0X020E0068,然后设置此寄存器,将 GPIO1_IO03 这个 IO 复用为 GPIO 功能,也就是 ALT5
3、配置 GPIO1_IO03(io口的属性配置)
找到 GPIO1_IO03 的配置寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03”的地址为 0X020E02F4,根据实际使用情况,配置此寄存器
4、设置 GPIO(输出还是输入)
5、控制 GPIO 的输出电平(电平是高还是低,低就是亮高是熄灭)
汇编程序代码
.global _start /* 全局标号 */
/*
* 描述: _start函数,程序从此函数开始执行此函数完成时钟使能、
* GPIO初始化、最终控制GPIO输出低电平来点亮LED灯。
*/
_start:
/* 例程代码 */
/* 1、使能所有时钟 */
ldr r0, =0X020C4068 /* CCGR0 寄存器地址*/
ldr r1, =0XFFFFFFFF /*表示对所有对外设都使能*/
str r1, [r0] /*赋值操作*/
ldr r0, =0X020C406C /* CCGR1 */
str r1, [r0]
ldr r0, =0X020C4070 /* CCGR2 */
str r1, [r0]
ldr r0, =0X020C4074 /* CCGR3 */
str r1, [r0]
ldr r0, =0X020C4078 /* CCGR4 */
str r1, [r0]
ldr r0, =0X020C407C /* CCGR5 */
str r1, [r0]
ldr r0, =0X020C4080 /* CCGR6 */
str r1, [r0]
/* 2、设置GPIO1_IO03复用为GPIO1_IO03 */
ldr r0, =0X020E0068 /* 将寄存器SW_MUX_GPIO1_IO03_BASE加载到r0中 */
ldr r1, =0X5 /* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */
str r1,[r0]
/* 3、配置GPIO1_IO03的IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 R0/6驱动能力
*bit [0]: 0 低转换率
*/
ldr r0, =0X020E02F4 /*寄存器SW_PAD_GPIO1_IO03_BASE */
ldr r1, =0X10B0
str r1,[r0]
/* 4、设置GPIO1_IO03为输出 */
ldr r0, =0X0209C004 /*寄存器GPIO1_GDIR */
ldr r1, =0X0000008
str r1,[r0]
/* 5、打开LED0
* 设置GPIO1_IO03输出低电平,也就算该寄存器对bit3
*/
ldr r0, =0X0209C000 /*寄存器GPIO1_DR */
ldr r1, =0
str r1,[r0]
/*
* 描述: loop死循环 ,b就是跳转的意思
*/
loop:
b loop
c语言代码
#ifndef __MAIN_H
#define __MAIN_H
/*************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : main.h
作者 : 左忠凯
版本 : V1.0
描述 : 时钟GPIO1_IO03相关寄存器地址定义。
其他 : 无
日志 : 初版V1.0 2019/1/3 左忠凯创建
*************************************/
/*
* CCM相关寄存器地址
*/
#define CCM_CCGR0 *((volatile unsigned int *)0X020C4068)
#define CCM_CCGR1 *((volatile unsigned int *)0X020C406C)
#define CCM_CCGR2 *((volatile unsigned int *)0X020C4070)
#define CCM_CCGR3 *((volatile unsigned int *)0X020C4074)
#define CCM_CCGR4 *((volatile unsigned int *)0X020C4078)
#define CCM_CCGR5 *((volatile unsigned int *)0X020C407C)
#define CCM_CCGR6 *((volatile unsigned int *)0X020C4080)
/*
* IOMUX相关寄存器地址
*/
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)
#define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)
/*
* GPIO1相关寄存器地址
*/
#define GPIO1_DR *((volatile unsigned int *)0X0209C000)
#define GPIO1_GDIR *((volatile unsigned int *)0X0209C004)
#define GPIO1_PSR *((volatile unsigned int *)0X0209C008)
#define GPIO1_ICR1 *((volatile unsigned int *)0X0209C00C)
#define GPIO1_ICR2 *((volatile unsigned int *)0X0209C010)
#define GPIO1_IMR *((volatile unsigned int *)0X0209C014)
#define GPIO1_ISR *((volatile unsigned int *)0X0209C018)
#define GPIO1_EDGE_SEL *((volatile unsigned int *)0X0209C01C)
#endif
#include "main.h"
/*
* @description : 使能I.MX6U所有外设时钟
* @param : 无
* @return : 无
*/
void clk_enable(void)
{
CCM_CCGR0 = 0xffffffff;
CCM_CCGR1 = 0xffffffff;
CCM_CCGR2 = 0xffffffff;
CCM_CCGR3 = 0xffffffff;
CCM_CCGR4 = 0xffffffff;
CCM_CCGR5 = 0xffffffff;
CCM_CCGR6 = 0xffffffff;
}
/*
* @description : 初始化LED对应的GPIO
* @param : 无
* @return : 无
*/
void led_init(void)
{
/* 1、初始化IO复用 */
SW_MUX_GPIO1_IO03 = 0x5; /* 复用为GPIO1_IO03 */
/* 2、、配置GPIO1_IO03的IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 R0/6驱动能力
*bit [0]: 0 低转换率
*/
SW_PAD_GPIO1_IO03 = 0X10B0;
/* 3、初始化GPIO */
GPIO1_GDIR = 0X0000008; /* GPIO1_IO03设置为输出 */
/* 4、设置GPIO1_IO03输出低电平,打开LED0 */
GPIO1_DR = 0X0;
}
/*
* @description : 打开LED灯
* @param : 无
* @return : 无
*/
void led_on(void)
{
/*
* 将GPIO1_DR的bit3清零
*/
GPIO1_DR &= ~(1<<3);
}
/*
* @description : 关闭LED灯
* @param : 无
* @return : 无
*/
void led_off(void)
{
/*
* 将GPIO1_DR的bit3置1
*/
GPIO1_DR |= (1<<3);
}
/*
* @description : 短时间延时函数
* @param - n : 要延时循环次数(空操作循环次数,模式延时)
* @return : 无
*/
void delay_short(volatile unsigned int n)
{
while(n--){}
}
/*
* @description : 延时函数,在396Mhz的主频下
* 延时时间大约为1ms
* @param - n : 要延时的ms数
* @return : 无
*/
void delay(volatile unsigned int n)
{
while(n--)
{
delay_short(0x7ff);
}
}
/*
* @description : mian函数
* @param : 无
* @return : 无
*/
int main(void)
{
clk_enable(); /* 使能所有的时钟 */
led_init(); /* 初始化led */
while(1) /* 死循环 */
{
led_off(); /* 关闭LED */
delay(500); /* 延时大约500ms */
led_on(); /* 打开LED */
delay(500); /* 延时大约500ms */
}
return 0;
}
注意:
#define CCM_CCGR0 *((volatile unsigned int *)0X020C4068) 是将右边地址命名为左边的
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068) 左边起名字是为了便于认识这个寄存器的作用,是将gpio1组的03复用为寄存器
第十一章 模仿 STM32 驱动开发格式实验
为了不用每个寄存器都定义一次,我们将同一个外设的所有寄存器都写成一个结构体,每个成员变量是每个io口对应的复用寄存器。然后通过IOMUX_SW_MUX->GPIO1_IO03”来访问 GPIO1_IO03 的 IO 复用寄存器了
第十二章 官方 SDK 移植实验
NXP 官方为 I.MX6ULL 编写了 SDK 包,在 SDK 包里面 NXP 已经编写好了寄存器定义文件, 所以我们可以直接移植 SDK 包里面的文件来用。
第十三章 BSP 工程管理实验
必须对工程文件做管理,将不同功能的源码文 件放到不同的目录中。另外我们也需要将源码文件中,所有完成同一个功能的代码提取出来放 到一个单独的文件中,也就是对程序分功能管理。
bsp 用来存放驱动文件;
imx6ul 用来存放跟芯片有关的文件,比如 NXP 官方的 SDK 库文件;
obj 用来存放编译生成的.o 文件;
project 存放 start.S 和 main.c 文件,也就是应用文件;
第十四章 蜂鸣器实验
当 SNVS_TAMPER1 输出低电平的时候 Q1 导通,相当于蜂鸣器的正 极连接到 DCDC_3V3,蜂鸣器形成一个通路,因此蜂鸣器会鸣叫。同理,当 SNVS_TAMPER1 输出高电平的时候 Q1 不导通,那么蜂鸣器就没有形成一个通路,因此蜂鸣器也就不会鸣叫
下面是调用了板子的函数库
void beep_init(void)
{
/* 1、初始化 IO 复用,复用为 GPIO5_IO01 */
IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0);
/* 2、配置 GPIO1_IO03 的 IO 属性 */
IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER1_GPIO5_IO01,0X10B0);
/* 3、初始化 GPIO,GPIO5_IO01 设置为输出 */
GPIO5->GDIR |= (1 << 1);
/* 4、设置 GPIO5_IO01 输出高电平,关闭蜂鸣器 */
GPIO5->DR |= (1 << 1):
}
第十六章 主频和时钟配置实验
时钟是单片机的脉搏,是单片机的驱动源,使用任何一个外设都必须打开相应的时钟。这样的好处是,如果不使用一个外设的时候,就把它的时钟关掉,从而可以降低系统的功耗
主频越大,马力越大
晶振产生时钟信号,时钟信号有平路
时钟树
时钟源就和水源一样,会流出不同的大小(频率)水流,然后驱动不同外设工作
第十七章 GPIO 中断实验
中断向量表,存放了中断程序的起始地址
是指中断服务程序入口地址的偏移量与段基址,一个中断向量占据4字节空间。中断向量表是8086系统内存中最低端1K字节空间,它的作用就是按照中断类型号从小到大的顺序存储对应的中断向量,总共存储256个中断向量。在中断响应过程中,CPU通过从接口电路获取的中断类型号(中断向量号)计算对应中断向量在表中的位置,并从中断向量表中获取中断向量,将程序流程转向中断服务程序的入口地址
中断向量偏移
一般ARM从0X000000000地址开始运行,对于STM32我们设置连接首地址为0X8000000。
如果代码一定要从0X8000000开始运行,那么需要告诉一下soc内核。也就是设置中断向量偏移。设置SCB的VTOR寄存器为新的中断向量表起始地址即可。
中断控制器
中断管理机构。使能和关闭指定的中断、设置中断优先级
Cortex-A7中断系统
1、Cortex-A中断向量表
Cortex-A中断向量表有8个中断,其中重点关注IRQ。Cortex-A的中断向量表需要用户自己去定义。
2、中断向量偏移
我们的裸机历程都是从0X87800000开始的,因此要设置中断向量偏移
GIC中断控制器
同NVIC一样,GIC用于管理Cortex-A的中断。GIC提供了开关中断,设置中断优先级。
4、IMX6U中断号
为了区分不同的中断,引入了中断号。ID0~ID15是给SGI,ID16~ID31是给PPI。剩下的ID32~1019给SPI,也就是按键中断、串口中断。。。。
6ULL支持128个中断
第十八章 EPIT 定时器实验
定时器
是内部soc的一个外设
定时器/计数器作为SoC的外设,主要用来实现定时执行代码的功能。定时器相对于SoC来说,就好像闹钟相对于人来说意义一样。单核的CPU是单线程的,只能干一件事情,干完这件事情完去干另一件事情需要定时器来提醒
2. 定时器有什么用
(1)定时器可以让SoC在执行主程序的同时,可以(通过定时器)具有计时功能,到了一定时间(计时结束)后,定时器会产生中断提醒CPU,CPU会去处理中断并执行定时器的ISR。从而去执行预先设定好的事件。
(2)定时器就好像是CPU的一个秘书一样,这个秘书专门管帮CPU来计时,并到时间后提醒CPU要做某件事情。所以CPU有了定时器之后,只需要预先把自己XX时间之后必须要做的事情绑定到定时器中断ISR即可,到了时间之后定时器就会以中断的方式提醒CPU来处理这个事情。
第19章 串口-UATR
在开发板上有一个串口转usb芯片,我们开发板与电脑相连是通过usb传输
全双工异步通用串行数据总线
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设置数据位,0的话表示7位数据位,1的话表示8位数据位。Bit6设置停止位,0的话表示1位停止位,1的话表示2位。Bit7奇偶校验位,为0的时候是偶校验,为1的时候是计校验。Bit8校验使能位,为0的时候关闭校验
UART电平标准
TTL 低电平 0 高电平1
RS-232 采用差分线 -3~·15 表示逻辑去 +3~+15表示0
第20章 ddr
DDR就是一个ram,就是内存,程序是在内存中运行的,先将文件存放到sd卡中,然后放到ddr中去运行
RAM 随机存储器(任意对其中的地址进行操作)
ROM 只读存储器 (存放数据)(flash也是)
第21 章 RGB-LCD
1、像素点
于一个“小灯“,不管是液晶屏,还是手机,平板,RGBLCD屏幕他都是有由一个个的彩色小灯构成的。彩色点阵屏每个像素点有三个小灯,红色、绿色和蓝色,也叫做RGB。RGB就是光的三原色。通过调整RGB三种颜色的比例,就可以实现姹紫千红的世界
2、分辨率
要想显示文字,图片,视频等等就需要很多个像素点,分辨率说的就是像素点的个数,1080P、720P、2K、4K,8K。1080P=1920*1080,表示一行有1920个像素点,一列有1080个。显示器有尺寸!24村,27村、55寸。尺寸不变的情况下,分辨率越高,显示效果越精细。4K=3840*2160相当于4个1080P
正点原子的RGB屏幕有:4.3寸480*272,800*480,7寸的800*480和1024*600,10.1寸的1280*800。
Iphone4屏幕尺寸是3.5寸,960*640分辨率,PPI=327
3、像素格式
如何将RGB三种颜色进行量化,每种颜色用8bit表示,RGB就需要888共24bit。可以描述出2^24=中颜色16777216=1677万种颜色。现在流行10bit,HDR10,支持HDR效果的10bit面板,RGB10 10 10。
在RGB888的基础上在加上8bit的ALPHA通道,也就是透明通道,ARGB8888=32位。
4、LCD屏幕接口
RGB格式的屏幕,一般叫做RGB接口屏。
屏幕接口有:MIPI、LVDS、MCU、RGB接口。
正点原子屏幕ID:使用ID可以识别出不同的屏幕,在RGBLCD屏幕上对R7,G7,B7焊接上拉或下拉电阻实现不同的ID。,
正点原子的ALPHA地板RGB屏幕接口用了3个3157模拟开关。原因是防止LCD屏幕上的ID电阻影响到6ULL的启动
屏幕接口类型
正点原子是rgb类型
正点原子屏幕ID,这个可以识别出不同的屏幕做出不同的初始化,这样板子就可以兼容多种屏幕
LCD参数
一帧的图像显示需要行同步信号与列同步信号一起运作
显存
一般是一帧图片需要的数据空间大小,比如32位表示一个像素点也急速4b,1024乘以00乘以4 = 2.5MB,我们直接定义一个数组lcdframe
IIC
2C 是很常见的一种总线协议,I2C 是 NXP 公司设计的,I2C 使用两条线在主控制器和从
机之间进行数据通信。一条是 SCL(串行时钟线),另外一条是 SDA(串行数据线),这两条数据
线需要接上拉电阻I2C 是支持多从机的,也就是一个 I2C 控制器下可以挂多个 I2C 从设备,这些不同的 I2C从设备有不同的器件地址,这样 I2C 主控制器就可以通过 I2C 设备的器件地址访问指定的 I2C设备了,一个 I2C 总线连接多个 I2C 设备如图 26.1.1.1 所示
起始位:SCL 为高电平的时候,SDA 出现下降沿就表示为起始位
停止位:SCL 位高电平的时候,SDA出现上升沿就表示为停止位
在数据传输的时候 ,要保证SCL高电平,SDA上的数据稳定
I2C 写时序
i2c的操作无非是读寄存器和写寄存器的操作
写时序的具体步骤
1、开始信号
2、发送I2C设备地址,每个i2c器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个i2c器件,这个一个8位的数据,其中高七位设备地址,最后一位是读写地址,为1的话就是表示这个一个读操作,为0的话就是表示这个一个写操作
3、I2C 器件地址后面跟着一个读写位,为 0 表示写操作,为 1 表示读操作
4、从机发送的 ACK 应答信号
5、重新发送开始信号
6、发送要写写入数据的寄存器地址
7、从机发送的 ACK 应答信号。
8、发送要写入寄存器的数据
9、从机发送的 ACK 应答信号
10、停止信号
I2C 读时序
l2C 单字节读时序比写时序要复杂一点,读时序分为 4 大步,第一步是发送设备地址,第
二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是 I2C 从器件输出要
读取的寄存器值
l2c多字节读写时序
多字节读写时序和单字节的基本一致,只是在读写数据的
时候可以连续发送多个自己的数据,其他的控制时序都是和单字节一样的
SPI
SPI 是 Motorola 公司推出的一种同步串行接口技术,是一种高速、全双工的同步通信总线,SPI 时钟频率相比 I2C 要高很多,最高可以工作
在上百 MHz。
标准的 4 线 SPI,这四根线如下
1、①、CS/SS,Slave Select/Chip Select,这个是片选信号线,用于选择需要进行通信的从设备。I2C 主机是通过发送从机设备地址来选择需要进行通信的从机设备的,SPI 主机不需要发送从机设备,直接将相应的从机设备片选信号拉低即可
2、SCK,Serial Clock,串行时钟,和 I2C 的 SCL 一样,为 SPI 通信提供时钟
3、MOSI/SDO,Master Out Slave In/Serial Data Output,简称主出从入信号线,这根数据线
只能用于主机向从机发送数据,也就是主机输出,从机输入
4、MISO/SDI,Master In Slave Out/Serial Data Input,简称主入从出信号线,这根数据线只
能用户从机向主机发送数据,也就是主机输入,从机输出
SPI 通信都是由主机发起的,主机需要提供通信的时钟信号。主机通过 SPI 线连接多个从
设备
SPI 有四种工作模式,通过串行时钟极性(CPOL)和相位(CPHA)的搭配来得到四种工作模式:
①、CPOL=0,串行时钟空闲状态为低电平。
②、CPOL=1,串行时钟空闲状态为高电平,此时可以通过配置时钟相位(CPHA)来选择具
体的传输协议。
③、CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
④、CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。