S32K3学习笔记---S32K3之LPSPI

本文详细介绍了在S32K3平台上的LPSPI通信,包括SPI的原理、同步和异步配置方法,以及如何在两个核上使用不同模式的SPI并配合DMA进行调试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

S32K3学习笔记—S32K3之LPSPI

1.前言

​ SPI是比较重要的一种通信方式,大多数复杂驱动和MCU之间都支持SPI通信,甚至还见过有需求是用SPI做一个bootloder,只要有想法皆可实现。本文主要是记录一下自己用过的几种SPI不同形式,以下是基于S32K324做的一个SPI通信的例子,会分别在两个核分别注册一路SPI,以及记录一下同步、异步、有无DMA的SPI方式。本文比较长,主要是想尽可能的把知道的都详细记录一下。

2.原理

​ SPI,是一种高速的,全双工,同步的通信总线, 主要用于MCU和外设进行通信, 有主从模式可配置,有4个外部接口,片选CS,时钟SCK,数据输入MISO,数据输出MOSI.

Block diagram

image-20231111213107381
​ 1.control logic:控制器参数和通信参数, 通过寄存器和TX FIFO配置

​ 2.shift register:将数据从总线移动到RX FIFO

​ 3.FIFO:发送和接收4byte的FIFO

​ 4.External SPI Interface : 四个外部接口

Register

image-20231111220151300
image-20231111220308473
​ 1.对于SPI的寄存器是必须要去看的,芯片手册上对每个寄存器都有解释,这里就不重复,芯片手册链接如下:S32K3XXRM.pdf

Sequence/Job/Channel

​ 这三者的关系必须要理清楚,Channel是软件层面的,与硬件的物理通道没关系。Channel只是对Bufff做一些配置。而Job是将Channel和外设绑定的。因此一个Channel可以同时属于多个Job。但如果管理不好,很容易造成buff里面的数据混乱。所以下面的例子和我自己实际用法都是Job和Channel一一对应。同样,一个Sequence下面可以有多个Job,并且Job是有优先级的。传输都是以Sequence为单位的,接收是具体到某个Channel。

TRAILINHG/LEADING

image-20231111202315358

​ 1.CPOL-时钟极性 : 时钟空闲时的电平, CPOL = 0,表示时钟空闲为低电平

​ 2.CPHA-时钟相位: 读取数据和发送数据的时钟沿, CPHA = 0,表示一个时钟周期内上升沿读取数据

​ 总结一下表格:

CPOL = 1CPHA = 0TRAILING
CPHA = 1LEADING
CPOL = 0CPHA = 0TRAILING
CPHA = 1LEADING

​ 如果接收的数据波形不对,可能是这个配置错了

3.EB配置

​ 根据SPI的模式不同其依赖的模块也不一样。如果是同步模式,其依赖于 MCU、PORT、SPI。如果是异步SPI 所用的模块包括,MCU、PORT、SPI、MCL、Platfom。

3.1 通用配置

​ 对于MCU模块来说,就比较简单,只需要做如下操作,添加两个给MCU模块的时钟就行,MCU其他的配置请参考:S32K3学习笔记—S32K3之MCU模块

​ 1.根据芯片手册可知,SPI0支持最高80M,SPI1-5支持最大40M,此处选择时钟源AIPS_PLAT_CLK和AIPS_SLOW_CLK

​ 其次是PORT模块,我们是要在两个核上各自注册一路SPI,因此相应的PORT口也需要注册到不同的核,具体操作如下:

image-20231111020738731

​ 1.此处名字,建议按之前的PORTA、B等来分,可参考:S32K3学习笔记—S32K3之MCU模块,此处为了方便记录和说明,单独把一路SPI的PIN单独建一个分组

​ 2.将core0的SPI的PORT全部配置一下

​ 接下就是PortPin的配置,可参考:S32K3学习笔记—S32K3之Gpt、Dio、Platform 来配置DIO、Platform, 下面简答配置一个

image-20231111021321575

​ 1. clk是输出方向

​ 2. PIN的模式,选LSPI0_LSPI0_SCK_OUT

​ 3. 默认低电平

image-20231111021723391

​ 1. 将这几个pin都分配到core0

​ 我们只需要按照这个步骤把SPI0的这几个Port都这样类似注册到core0,模式、MSCR及输出方向别设置错就行。值得注意的一点就是CS的初始化默认电平,需要根据时钟空闲电平来设置。这个需要看具体的从芯片的SPI时序才能确认。

同理将SPI1的port分配到core1就行。

image-20231111023311247

​ 至此,基本的通用配置就完成了,接下来就是分模式来配置两种SPI。

3.2 同步SPI配置

​ 同步SPI我们就用的比较的简单,不用DMA的形式,当然也可以选择用DMA,具体看需求。

image-20231111094459868

​ 1.多核使能

​ 2.SPI通道buffers支持的种类。 0—IB 、1—EB 、 2—both IB and EB

​ 3.是否使能中断处理sequences

​ 4.SpiLevel ,此处先配置同步。 0— 同步、 1—异步、2—同步&异步

​ 5.是否对不同的 sequences并发调用Spi_SyncTransmit()

​ 6.是否使能DMA

image-20231111100102726

​ 1.选择EB还是IB, 需要和前面的SpiChannelBuffersAllowed对应

​ 2.传输数据的宽度,这是从芯片决定的,按照从芯片SPI的数据位来确定,1~64bits,需要8个bit对齐,例如配置12bit,那么在传输的时候,我们只能取低12位,高四位忽略。

​ 3.只有选择EB,此处才可以动态配置数据缓冲区的最大宽度,单位byte

​ 4.数据传输的第一个起始位是高位还是低位,也是根据从芯片来选择

​ 5.将SPI资源分配到核0

​ 接下来就是外设驱动配置,这个地方主要是根据从芯片来配置,

image-20231111105652547

​ 1.波特率设置,根据芯片和实际需求设置

​ 2.片选通道选择

​ 3.片选传输时的极性

​ 4、6.使能是否需要自动拉低片选,如果使能就不需要手动拉片选了

​ 5.数据传输的极性,LEADING、TRAILING,具体选择原理中有奖

​ 7.硬件通道,CSIB0可以理解为SPI0

​ 8.时钟空闲电平

image-20231111110358407

​ 1.将此SPI的分配到核0

​ 在配置完channel和ExternalDevice之后,就需要将这两个联系起来,SpiJob就可以将两者联系起来。具体配置如下:

image-20231111125553652

​ 1.SpiJob优先级,0最低,3最高

​ 2.此SpiJob对外部设备的引用

image-20231111130131072

​ 1.对于SpiChannelList中,主要是引用SpiChannel,通过SpiJob联系Spichannel和SpiExternalDevice

image-20231111135916149

​ 1.对于同步来说,我们只需要配置SpiSequenceId就行

​ 2.是否使能DMA传输
image-20231111140052543

​ 1.这个地方是联系SpiJob和SpiSequence的,每个SpiSequence可以有很多个Job

image-20231111140441410

​ 1.选择SPI通道

​ 2.SPI的主从模式

​ 3.是否为同步传输。如使能只能进行同步传输

​ 4.时钟参考,这个地方适合MCU模块建立连接的

​ 至此,core0的这一路SPI算是配置完成。类似的我们可以在core1配置另外一路SPI,没啥区别,就是将资源分配到core1和除SPI0外的SPI的时钟是40M,具体配置如下:

image-20231111141002519

​ 1.其他的和core0一直,这个地方将SPIchannel注册到core1

image-20231111141140336

​ 1.SpiExternalDevice注册到core1

image-20231111141306765

​ 1.SPI1_5的时钟和SPI0的不一致,此次选择SPI1_5

​ SPI同步的形式就配置完成,可以的做个验证。

3.3 异步SPI配置

​ 可能比较大一点的项目都是同步+异步的形式,下面将记录一下异步SPI将用DMA的形式,其关联模块包括Mcl、Platform

​ 首先是Platform模块,使能DMA和SPI的中断。image-20231111182607000

​ 1.使能对应的DMA和SPI中断

​ DMA先简单配置,先用起来,后续专门来记录这个DMAimage-20231111182725600

​ 1.多核使能

​ 2.用于初始化和取消初始化Mcl驱动程序的分区

​ 3.使能DMA

image-20231111183327440

​ 1.dmaLogicInstance_ConfigType默认配置就行,分配到core0

image-20231111185315035

​ 1.硬件通道选择

​ 2.注册DMA回调函数

​ 3.注册到core1

​ 4.使能全局配置

image-20231111185704043

​ 1.使能DMA请求,其他的保持默认配置就好

image-20231111180618999

​ 1.EB&IB

​ 2.同步&异步

​ 3.使能DMA

image-20231111180847044

​ 1.异步模式配置一个回调通知函数,代码需要自注册
image-20231111181043688

​ 1.失能同步传输

​ 2.使能异步传输

​ 3.配置DMA的逻辑通道

​ 至此,异步SPI的配置就完成了,接下来就是调试,可以简单的测试。

4.代码调试

4.1 同步SPI调试

​ 对于同步传输,最简单的就是找到一个从芯片的可读可写的一个寄存器,直接先写一下寄存器再读一下寄存器,看是否SPI通信成功。

int main_c0(void)
{
	Spi_DataBufferType TxChBuf0[4] = {0x10,0x11,0x0,0x12};
    Spi_DataBufferType RxChBuf0[4];
    /* Init clock */
#if (STD_ON == MCU_PRECOMPILE_SUPPORT)
    Mcu_Init(NULL_PTR);
#else
    Mcu_Init(&Mcu_Config_VS_0);
#endif

#if (STD_ON == MCU_INIT_CLOCK)
    /* Initialize the clock tree and apply PLL as system clock */
    Mcu_InitClock(McuClockSettingConfig_0);

#if (STD_OFF == MCU_NO_PLL)
    while ( MCU_PLL_LOCKED != Mcu_GetPllStatus() )
    {
        /* Busy wait until the System PLL is locked */
    }
    Mcu_DistributePllClock();
#endif

    Mcu_SetMode(McuModeSettingConf_0);

#else
    #error "The Mcu Init Clock API should be enabled from the Mcu driver"
#endif

    /* Initialize all pins using the Port driver */
#if (STD_ON == PORT_PRECOMPILE_SUPPORT)
    Port_Init(NULL_PTR);
#else
    Port_Init(&Port_Config_VS_0);
#endif

    /* Initialize Platform driver */
    Platform_Init(NULL_PTR);
    
    Spi_Init(NULL_PTR);

    /* Set up external buffer to transmission and reception */
    Spi_SetupEB(SpiConf_SpiSequence_SpiSequence_0, TxChBuf0, RxChBuf0, 4);

    /* This sequence of slave: transferring 10 frame 16 bits using Dma */
    Spi_SyncTransmit(SpiConf_SpiSequence_SpiSequence_0);

    while (SPI_SEQ_OK != Spi_GetSequenceResult(SpiConf_SpiSequence_SpiSequence_0));
	
	for(;;)
	{
		/* do nothing */
	}


    return (0U);
}
int main_c1(void)
{
	Spi_DataBufferType TxChBuf1[4] = {0x10,0x11,0x0,0x12};
    Spi_DataBufferType RxChBuf1[4];
    /* Initialize all pins using the Port driver */
#if (STD_ON == PORT_PRECOMPILE_SUPPORT)
    Port_Init(NULL_PTR);
#else
    Port_Init(&Port_Config_VS_0);
#endif
	
    /* Initialize Platform driver */
    Platform_Init(NULL_PTR);
    
    Spi_Init(NULL_PTR);

    /* Set up external buffer to transmission and reception */
    Spi_SetupEB(SpiConf_SpiSequence_SpiSequence_1, TxChBuf1, RxChBuf1, 4);

    /* This sequence of slave: transferring 10 frame 16 bits using Dma */
    Spi_SyncTransmit(SpiConf_SpiSequence_SpiSequence_1);

    while (SPI_SEQ_OK != Spi_GetSequenceResult(SpiConf_SpiSequence_SpiSequence_1));
	
	for(;;)
	{
		/* do nothing */
	}

    return (0U);
}

​ SPI就是要么一步成功,要么从头查起,一旦出问题就得拿个示波器去抓时序波形,波形是最直观的,也是最快能定位问题的。

4.2异步SPI调试

​ core0的操作和同步的一样,这只记录一下core1的调试。

int main_c1(void)
{
	Spi_DataBufferType TxChBuf1[4] = {0x10,0x11,0x0,0x12};
    Spi_DataBufferType RxChBuf1[4];
    /* Initialize all pins using the Port driver */
#if (STD_ON == PORT_PRECOMPILE_SUPPORT)
    Port_Init(NULL_PTR);
#else
    Port_Init(&Port_Config_VS_0);
#endif
	
    /* Initialize Platform driver */
    Platform_Init(NULL_PTR);
    
    Spi_Init(NULL_PTR);
/*******************************the first*****************************************/
    /* Set up external buffer to transmission and reception */
    Spi_SetupEB(SpiConf_SpiSequence_SpiSequence_1, TxChBuf1, RxChBuf1, 4);

    /* This sequence of slave: transferring 10 frame 16 bits using Dma */
    Spi_AsyncTransmit(SpiConf_SpiSequence_SpiSequence_1);

    while (SPI_BUSY != Spi_axSpiHwUnitQueueArray[0].Status)
    {
    	Spi_MainFunction_Handling();
    }
/*******************************the first*****************************************/

/*******************************The second*****************************************/
	/* Using interrupt in transfer */
    Spi_SetAsyncMode(SPI_INTERRUPT_MODE);
    
	/* Set up external buffer to transmission and reception */
    Spi_SetupEB(SpiConf_SpiSequence_SpiSequence_1, TxChBuf1, RxChBuf1, 4);

 	/* This sequence of slave: transferring 10 frame 16 bits using Dma */
    Spi_AsyncTransmit(SpiConf_SpiSequence_SpiSequence_1);
    
    /*The next steps are all in the DMA_ISR*/
/*******************************The second*****************************************/


	for(;;)
	{
		/* do nothing */
	}

    return (0U);
}


void SPI_CallBack2(void)
{
	Spi_HWUnitType HWUnit = 0;
	uint8 txchn = 0,rxchn = 0;
	for(;HWUnit < (Spi_HWUnitType) SPI_MAX_HWUNIT;HWUnit++)
	{
		if(TRUE == Spi_apxSpiConfigPtr[Spi_GetCoreID]->HWUnitConfig[HWUnit].PhyUnitConfig->IpConfig.LpspiIpConfig->DmaUsed)
		{
			txchn = Spi_apxSpiConfigPtr[Spi_GetCoreID]->HWUnitConfig[HWUnit].PhyUnitConfig->IpConfig.LpspiIpConfig->TxDmaChannel;
			rxchn = Spi_apxSpiConfigPtr[Spi_GetCoreID]->HWUnitConfig[HWUnit].PhyUnitConfig->IpConfig.LpspiIpConfig->RxDmaChannel;

			Mcl_SetDmaChannelCommand(txchn, DMA_IP_CH_CLEAR_DONE);
			Mcl_SetDmaChannelCommand(rxchn, DMA_IP_CH_CLEAR_DONE);
		}
		else
        {
			/* do nothing*/
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值