I.MX6ULL 中,SPI 对应的模块是ECSPI(Enhanced Configurable SPI)
目录
3.8 ECSPI3_PERIODREG(选择时钟源、采样周期)
一、硬件原理图分析
SPI 至少涉及到四个引脚,在开发板底板原理图找到 ECSPI 相关的四个引脚,以及这四个引脚关联到核心板的哪些引脚。比如下面的 SS0 是片选信号线,关联到核心板的 UART2_TXD
- SS0:片选信号 —— UART2_TXD
- SCLK:时钟线 —— UART2_RXD
- MISO:主设备接收 —— UART2_RTS
- MOSI:主设备发送 —— UART2_CTS
注意:IMX6ULL 有四个 SPI 主控制器 ECSPI1~ECSPI4,每个主控制器包含四个片选引脚 SS0~SS3
二、IO 复用寄存器解析
以 SS0 为例,关联的核心板引脚是 UART2_TXD,UART2_TXD可能有多种用途,我们要将其复用为 SS0 功能。(其他引脚也是同理)
- 设置复用的寄存器:IOMUXC_SW_MUX_CTL_PAD_UART2_TX_DATA
- 设置电气属性的寄存器:IOMUXC_SW_PAD_CTL_PAD_UART2_TX_DATA
- 初始值:0x10B1
/*************** SS0 复用初始化 ******************/
寄存器(基地址): IOMUXC_SW_MUX_CTL_PAD_UART2_TX_DATA (0x20E0094)
寄存器(基地址): IOMUXC_SW_PAD_CTL_PAD_UART2_TX_DATA (0x20E0320)
初始化操作:
IOMUXC_SW_MUX_CTL_PAD_UART2_TX_DATA &=~ (0xF); // 低4位清零
IOMUXC_SW_MUX_CTL_PAD_UART4_TX_DATA |= 8; // 复用为SS0
IOMUXC_SW_PAD_CTL_PAD_UART2_TX_DATA = 0x10B1;
/*************** SCL 复用初始化 ******************/
寄存器(基地址): IOMUXC_SW_MUX_CTL_PAD_UART2_RX_DATA (0x20E0098)
寄存器(基地址): IOMUXC_SW_PAD_CTL_PAD_UART2_RX_DATA (0x20E0324)
初始化操作:
IOMUXC_SW_MUX_CTL_PAD_UART2_RX_DATA &=~ (0xF); // 低4位清零
IOMUXC_SW_MUX_CTL_PAD_UART2_RX_DATA |= 8; // 复用为SCL
IOMUXC_SW_PAD_CTL_PAD_UART2_RX_DATA = 0x10B1;
/*************** MISO 复用初始化 ******************/
寄存器(基地址): IOMUXC_SW_MUX_CTL_PAD_UART2_RTS_B (0x20E00A0)
寄存器(基地址): IOMUXC_SW_PAD_CTL_PAD_UART2_RTS_B (0x20E032C)
初始化操作:
IOMUXC_SW_MUX_CTL_PAD_UART2_RTS_B &=~ (0xF); // 低4位清零
IOMUXC_SW_MUX_CTL_PAD_UART2_RTS_B |= 8; // 复用为MISO
IOMUXC_SW_PAD_CTL_PAD_UART2_RTS_B = 0x10B1;
/*************** MOSI 复用初始化 ******************/
寄存器(基地址): IOMUXC_SW_MUX_CTL_PAD_UART2_CTS_B (0x20E009C)
寄存器(基地址): IOMUXC_SW_PAD_CTL_PAD_UART2_CTS_B (0x20E0328)
初始化操作:
IOMUXC_SW_MUX_CTL_PAD_UART2_CTS_B &=~ (0xF); // 低4位清零
IOMUXC_SW_MUX_CTL_PAD_UART2_CTS_B |= 8; // 复用为MOSI
IOMUXC_SW_PAD_CTL_PAD_UART2_CTS_B = 0x10B1;
三、ECSPI 寄存器解析
3.1 时钟配置
我们需要配置 CSCDR2 寄存器的 ECSPI_CLK_SEL 位(bit 18),选择时钟源为 pll3_sw_clk 八分频以后的 60MHz。CSCDR2 的 ECSPI_CLK_PODF 设为 1 分频。
寄存器: CCM_CSCDR2
基地址: 0x20C4038
初始化操作:
/*
* bit 18: 0 选择时钟源为 60MHz
* bit 24-19: 000000 设为1分频
*/
CCM_CSCDR2 &= ~(1 << 18);
CCM_CSCDR2 &= ~(0x3F << 19);
3.2 ECSPI3_RXDATA
SPI 是全双工通信,可以同时进行收发。ECSPIx_RXDATA 是用于保存接收数据的寄存器。本质就是从一个 RXFIFO队列的队首取出一个字(32bit)的数据,RXFIFO(类似于队列)的大小为 64 × 32。
下面这个示意图可以理解为 RXFIFO(接收队列),每次读一行。
注意:ECSPI_STATREG 的 RR 位一旦被清零,从 RXFIFIO 读取到的数据是无意义的。
寄存器: ECSPI3_RXDATA
基地址: 0x2010000
3.3 ECSPI3_TXDATA
SPI 是全双工通信,可以同时进行收发。ECSPIx_TXDATA 是用于保存发送内容的寄存器,本质是向 TXFIFO 队列的队尾插入一个字(32bit)的数据。只要队列未满,即便是在对方接收数据的时候,也可以写入数据。
下面这个示意图可以理解为 TXFIFO(发送队列),每次写一行。
注意:写入一个字,并不代表就会传输一个字的数据。 传输多少 bit 的数据是由ECSPIx_CONREG寄存器的 BURST_LENGTH 位决定的。
寄存器: ECSPI3_TXDATA
基地址: 0x2010004
3.4 ECSPI3_CONREG(ECSPI使能、分频)
bit 0: SPI 使能(0: 关闭 1: 使能)
bit 2: 用于控制什么时候可以开始突发式访问,简单说就是什么时候可以开始传输数据,仅在主机模式下适用(0: 空闲状态下 1: 开始写入或者读取的时候)
bit 3: 开始模式控制,仅在主机模式下适用。0 表示通过 XCH 位 (bit 2) 和 SS_CTL 位来控制开始传输的时间点;1 表示一旦有数据写入 TXFIFO 的时候就开始传输。
bit 7- 4: 每个主控制器有四个片选通道,这里我们使用的是 SS0,该位控制的是本次通信,MCU是作为主设备还是从设备,来和片选通道的设备进行通信的。一般需要搭配 bit 19 - 18 使用(0: 从设备 1: 主设备)
bit 11 - 8(PRE_DIVIDER): ECSPI 模块在内部还会进行两次分频。这里设置的是第二次分频的分频值(分频值为n)
bit 15 - 12(POST_DIVIDER): ECSPI 模块在内部还会进行两次分频。这里设置的是第一次分频的分频值(分频值为2^n)
bit 17 - 16: SPI_RDY 信号控制位。(0: 无视SPI_RDY信号 1: SPI_RDY 信号为边沿触发 2: SPI_DRY 为电平触发)
bit 19 - 18: 一个 ECSPI 控制器有四个片选通道,这里使用的是 SS0,该位控制的是要选择和哪个片选通道通信,还需通过 bit 7-4 来选择主从设备。
bit 31 - 20: 每次实际传输的数据长度。写入数据的长度 ≠ 实际传输的数据长度,如果你写入数据的长度 > 实际传输的长度,多余部分会留到下一次传输。
寄存器: ECSPI3_CONREG
基地址: 0x2010008
3.5 ECSPI3_CONFIGREG(配置寄存器)
bit 3 - 0: 相位选择。ECSPI 有四个通道,我们使用的是通道 0。需要保证主从设备的相位、极性一致(0: CPHA=0 1: CPHA=1)
bit 7 - 4: 极性选择。ECSPI 有四个通道,我们使用的是通道 0。需要保证主从设备的相位、极性一致(0: CPOL=0 1: CPOL=1)
bit 15 - 12: 某个通道的SPI 片选信号极性设置。(0: 该通道片选信号低电平有效 1: 该通道片选信号高电平有效)
bit 19 - 16: 数据控制。该字段控制的是数据处于非活跃状态时的各个通道的电平。一般设为 0(0: 高电平 1: 低电平)
bit 23 - 20: 时钟控制。该字段控制的是非活跃状态时的各个通道的电平.一般设为 0(0: 高电平 1: 低电平)
寄存器: ECSPI3_CONFIGREG
基地址: 0x201000C
3.6 ECSPI3_INTREG(中断使能寄存器)
bit 0: TXFIFO 为空时,中断触发使能(0: 关闭 1: 使能)
bit 1: TXFIFO 有数据进入,但是数据长度小于TX_THRESHOLD,中断触发使能(0: 关闭 1: 使能)
bit 2: TXFIFO 为满时,中断触发使能(0: 关闭 1: 使能)
bit 3: RXFIFO 有数据就绪时,中断触发使能(0: 关闭 1: 使能)
bit 4: RXFIFO 有数据进入,但是数据长度小于RX_THRESHOLD,中断触发使能(0: 关闭 1: 使能)
bit 5: RXFIFO 为满时,中断触发使能(0: 关闭 1: 使能)
bit 6: RXFIFO 溢出时,中断触发使能(0: 关闭 1: 使能)
bit 7: 数据传输完成时,中断触发使能(0: 关闭 1: 使能)
寄存器: ECSPI3_INTREG
基地址: 0x2010010
3.7 ECSPI3_STATREG(状态寄存器)
bit 0: TXFIFO是否为空(0: TXFIFO 不为空 1: TXFIFO 为空)
bit 1: TXFIFO中的数据量是否超过TX_THRESHOLD(0: 是 1: 否)
bit 2: TXFIFO 是否满了(0: TXFIFO 没满 1: TXFIFO 已满)
bit 3: RXFIFO 是否有数据就绪(0: 尚未有数据就绪 1: 已有数据就绪)
bit 5: RXFIFO 是否满了(0: RXFIFO 没满 1: RXFIFO 已满)
bit 6(RO): RXFIFO 数据溢出(0: 没有溢出 1: 已经溢出)
- 数据溢出:当接收的数据量超过 RXFIFO 的大小,SPI为了保证数据不会被覆盖,会舍弃最新接收的数据,此时溢出标志位 RO 会被置1
bit 7: 数据是否传输完毕(0: 数据传输中 1: 数据传输完毕)
寄存器: ECSPI3_STATREG
基地址: 0x2010018
3.8 ECSPI3_PERIODREG(选择时钟源、采样周期)
选择时钟源指的是选择系统高频时钟还是其他低频时钟;采样周期指的是相邻两次连续读 / 写数据之间的之间间隔, 假设SPI 的时钟是 10MHz。
如果采样周期数是 0x2000,说明在上一次读 / 写完毕以后要等待 0x2000 个周期才开始下一次读 / 写。等待的时间 = (1 / 10000000) * 0x2000 = 0.0008192 s = 0.8192 ms,即连续读取数据的时间间隔为 0.8 ms
bit 21-16: 设置片选信号和第一个SPI时钟周期产生时的时间差。
bit 15: 选择时钟源。这里我们选择 SPI Clock,这里的SPI Clock 并不是系统时钟源,而是经过SPI内部分频以后得到的时钟源(0: SPI 时钟源 1: 低频时钟)
bit 14-0: 设置采样周期。相邻两次读 / 写之间的时间间隔。
寄存器: ECSPI3_PERIODREG
基地址: 0x201001C