IMX6ULL SPI控制器

发布时间 2023-10-22 16:49:16作者: fuzidage

1 IMX6ULL SPI控制器

NXP的6ull参考手册第Chapter 20介绍了SPI控制器,Enhanced Configurable SPI (ECSPI) 。

1.1 特点

①、全双工同步串行接口。
②、可配置的主/从模式。
③、四个硬件片选信号,支持多从机。
④、发送和接收都有一个 32x64 的 FIFO。
⑤、片选信号 SS/CS,时钟信号 SCLK 的极性相位(CPOL,CPHA)可配置。
⑥、支持 DMA
⑦、SCK最高可以到输入参考时钟高达60Mhz

1.2 框图

image
最右边是引脚,SCLK,MISO,MOSI等,上面是外围总线,通过APB总线进行寄存器读写,INTREG,CONREG等等。TXDATA和TXDATA寄存器存放了要发送的数据和接收的收据。
时钟源来自Reference Clock or Low Frequency Clock。可选时钟源如下:这里选用ecspi_clk_root。
image
image
① CSCDR2的ECSPI_CLK_SEL位设置为0,选择出PLL3_SW_CLK 进行8分频作为 ECSPI 根时钟源。PLL3_SW_CLK=480MHz,8分频就是60MHz。
② CSCDR2 的 ECSPI_CLK_PODF位再次进行分频,ECSPI_CLK_PODF位设置成0,表示2^0分频,也就是1分频。
③ 最后ECSPI_CLK_ROOT就为60MHz

1.3 时序

CPOL时钟极性 和CPHA时钟相位组合成了4种模式:

CPOL:表示SPI CLK的初始电平(空闲状态时电平),0为低电平,1为高电平
CPHA:表示相位,即第一个还是第二个时钟沿采样数据,0为第一个时钟沿,1为第二个时钟沿

image

2 IMX6ULL SPI控制器寄存器描述

控制器初始化流程:
CONREG[EN]:复位,0表示复位
CCM开启ECSPI时钟
CONREG[EN]:复位,1表示反选复位
image
RXDATA寄存器:接收数据寄存器,RR位的状态决定接受数据是否就绪
image

TXDATA寄存器:发送数据寄存器,实际传输的位数由相应SPI控制寄存器的BURST_LENGTH位来决定。
image

CONREG寄存器:控制寄存器
image
EN:使能位,1为使能
SMC:为1表示当数据写入TXFIFO时,立即启动SPI突发;这里使用该模式
CHANNEL_MODE:硬件片选模式选择,bit[7:4]分别表示通道3到通道0,这里采用通道0设定为Master mode.因此bit[7:4]配置成1
POST_DIVIDER:后分频,0到15表示2^n次方分频,比如0就是1分频,15就是2^15分频
PRE_DIVIDER:前分频,0到15表示1到16分频
前面spi clk的时钟源为ECSPI_CLK_ROOT 60MHz,这里我们用6MHz,因此可以设置POST_DIVIDER=0,PRE_DIVIDER=9,表示10分频。
CHANNEL_SELECT:通道选择,也就是硬件片选SS选择,这里选择SS0,通道0
BURST_LENGTH:突发访问长度,这里我们用一次突发8bit, 配置成0x7

CONFIGREG寄存器:配置寄存器
image
SCLK_PHA:时钟相位,SCLK_PHA[3:0]分别对应通道3~0,设置为0表示第一个时钟沿采集数据,设置成1表示第二个时钟沿采集数据。(同POL组成4种模式)
SCLK_POL:时钟极性,表示时钟初始空闲时的电平,0为低电平,1为高电平。(同PHA组成4种模式)
SS_CTL:硬件片选的wave form select,这个用不上设置成0
SS_POL:硬件片选的极性选择,用不上设置成0
DATA_CTL:数据线空闲时电平状态,我们设置成0表示高电平
SCLK_CTL:时钟线空闲时电平状态,我们设置成0表示低电平(POL设置了时钟初始空闲时的电平为低电平)
HT_LENGTH: HT Mode不用,无需配置

STATREG寄存器:状态寄存器
image
TE:TXFIFO empty, 为1表示TXFIFO为空,0表示TXFIFO还没空,因此往TXDATA发送数据时,需要先等待TXFIFO为空。
RR: RXFIFO Ready,1表示有数据,0表示数据还没ready.读取RXDATA需要等RXFIFO先ready。

PERIODREG寄存器:采样周期寄存器
image
SAMPLE_ PERIOD:突发访问时的等待周期,表示等待多少个时钟周期后进行一下次突发访问。我们设置为0x2000。
image
CSRC: 等待周期的单位,0表示以SPI clk为单位, 1表示以low-frequency reference clk 32.768KHz为单位。
CSD_CTL:硬件片选延时,表示片选后多少个时钟周期才可以进行数据传输。(这里不用,我们用软件片选)

3 IMX6ULL SPI控制器代码编写

void spi_init(ECSPI_Type *base)
{
	/* 配置CONREG寄存器
	 * bit0 : 		1 	使能ECSPI
	 * bit3 : 		1	当向TXFIFO写入数据以后立即开启SPI突发。
	 * bit[7:4] : 	0001 SPI通道0主模式,根据实际情况选择,
	 *            	   	开发板上的ICM-20608接在SS0上,所以设置通道0为主模式
	 * bit[19:18]:	00 	选中通道0(其实不需要,因为片选信号我们我们自己控制)
	 * bit[31:20]:	0x7	突发长度为8个bit。 
	 */
	base->CONREG = 0; /* 先清除控制寄存器 */
	base->CONREG |= (1 << 0) | (1 << 3) | (1 << 4) | (7 << 20); /* 配置CONREG寄存器 */

	/*
	 * ECSPI通道0设置,即设置CONFIGREG寄存器
	 * bit0:	0 通道0 PHA为0
	 * bit4:	0 通道0 SCLK高电平有效
	 * bit8: 	0 通道0片选信号 当SMC为1的时候此位无效
	 * bit12:	0 通道0 POL为0
	 * bit16:	0 通道0 数据线空闲时高电平
	 * bit20:	0 通道0 时钟线空闲时低电平
	 */
	base->CONFIGREG = 0; 		/* 设置通道寄存器 */

	/*  
	 * ECSPI通道0设置,设置采样周期
	 * bit[14:0] :	0X2000  采样等待周期,比如当SPI时钟为10MHz的时候
	 *  		    0X2000就等于1/10000 * 0X2000 = 0.8192ms,也就是连续
	 *          	读取数据的时候每次之间间隔0.8ms
	 * bit15	 :  0  采样时钟源为SPI CLK
	 * bit[21:16]:  0  片选延时,可设置为0~63
	 */
	base->PERIODREG = 0X2000;		/* 设置采样周期寄存器 */

	/*
	 * ECSPI的SPI时钟配置,SPI的时钟源来源于pll3_sw_clk/8=480/8=60MHz
	 * 通过设置CONREG寄存器的PER_DIVIDER(bit[11:8])和POST_DIVEDER(bit[15:12])来
	 * 对SPI时钟源分频,获取到我们想要的SPI时钟:
	 * SPI CLK = (SourceCLK / PER_DIVIDER) / (2^POST_DIVEDER)
	 * 比如我们现在要设置SPI时钟为6MHz,那么PER_DIVEIDER和POST_DEIVIDER设置如下:
	 * PER_DIVIDER = 0X9。
	 * POST_DIVIDER = 0X0。
	 * SPI CLK = 60000000/(0X9 + 1) = 60000000=6MHz
	 */
	base->CONREG &= ~((0XF << 12) | (0XF << 8));	/* 清除PER_DIVDER和POST_DIVEDER以前的设置 */
	base->CONREG |= (0X9 << 12);					/* 设置SPI CLK = 6MHz */
}

/*
 * @description		: SPI通道0发送/接收一个字节的数据
 * @param - base	: 要使用的SPI
 * @param - txdata	: 要发送的数据
 * @return 			: 无
 */
unsigned char spich0_readwrite_byte(ECSPI_Type *base, unsigned char txdata)
{ 
	uint32_t  spirxdata = 0;
	uint32_t  spitxdata = txdata;

	/* 选择通道0 */
	base->CONREG &= ~(3 << 18);
	base->CONREG |= (0 << 18);

	while((base->STATREG & (1 << 0)) == 0){} /* 等待发送FIFO为空 */
		base->TXDATA = spitxdata;

	while((base->STATREG & (1 << 3)) == 0){} /* 等待接收FIFO有数据 */
		spirxdata = base->RXDATA;
	return spirxdata;
}