S32K148 AUTOSAR SPI 摸索学习&开发

1.项目背景

1.使用S32K148作为主设备,通过SPI通信来配置作为从设备的TJA1145.
2.使用S32K148作为主设备,通过SPI通信与另一个芯片进行片间通信.
3.使用S32K148作为从设备,通过SPI通信与另一个芯片进行片间通信.(不在我这里做)

2.基础知识

2.1 SPI前置知识点

SPI是全双工主从通信方式
--全双工:两根数据线->可同时收发
--主从:支持多主多从,但大多情况为一对一或一对多
因为S32K1有3路SPI 本项目用一对一即可

2.2 物理线的连接

一般来说SPI接线有3根和4根
由于我的项目是4根线,所以只关注4根线

MASTER -- SLAVE
CS ----------> CS
SCLK -------> SCLK
MOSI ------> SDI
MISO <------ SDO

*CS 片选信号 当从设备的CS被拉高时代表被主设备选中,即将传输数据
*SCLK 时钟信号 主设备提供稳定时钟给从设备保证SPI时序
*MOSI Master Output Slave Input 直译 主设备发送从设备接收.当芯片做主设备时用来发送数据
*MISO Master Iutput Slave Output 直译 主设备接收从设备发送.当芯片做主设备时用来接收数据

2.2.1 SCLK

*CPOL 极性 MOSI和MISO空闲时的电平状态
*CPHA 相位 MOSI和MISO是在SCLK的下降沿采样还是上升沿采样?

MOSI和MISO 后面统称数据线

二者组合便有四种状态
CPOL|CPHA = 0x0 在SCLK被拉高时进行取值,若此时数据线为高电平则记为1,低电平为0
CPOL|CPHA = 0x1 在SCLK被拉低时进行取值,若此时数据线为高电平则记为1,低电平为0
CPOL|CPHA = 0x2 在SCLK被拉高时进行取值,若此时数据线为低电平则记为1,高电平为0
CPOL|CPHA = 0x3 在SCLK被拉低时进行取值,若此时数据线为低电平则记为1,高电平为0

2.2.2 CS

2.2.2.1 一主对一从的情况下

主的CS连接从的CS.当需要传输数据时提前拉低CS即可

2.2.2.2 一主对多从的情况下

此时需要将多从的CS引脚接到主的GPIO上
那么会面临潜在的问题:当从设备增加时,对GPIO的占用量也会增加
提供以下思路来减少对GPIO的使用开销:
1.解码器:主设备提供3 GPIO可以分出8 GPIO,算是外挂芯片导致+成品
2.引脚复用:沟通不那么频繁的设备可以挂在使用频率不高的支持复用引脚上,开发折磨
3.菊花链:只需一个CS引脚,但是发一条消息所有从设备都折腾一遍
4.SPI集线器:很好用但加成本,MCU只需对接集线器即可

2.2.2.3 多主对多从的情况下

虽然理论上可行但是SPI不太适合多主 SPI多主多从过于复杂,维护成本高.开发难度大.故不做思考.

3 AUTOSAR工程配置-基于EB Tresos配置MCAL(RTD4.4)的SPI驱动

AUTOSAR架构需要MCAL作为底层硬件的驱动,这里使用eb来配置RTD驱动.

3.1 Port配置

根据原理图和2.2提到的输入输出方向即可.
因为S32K148提供不只一路SPI 所以配置2路
1.TJA1145的4个脚分别是 SDO SDI SCK(SCLK) SCSN(CS)
-->对应S32K1的 PTB25/LPSPI2_PCS0(CS) PTB27/LPSPI2_SOUT(MOSI) PTB28/LPSPI2_SIN(MISO) PTB29/LPSPI2_SCK(SCLK)

2.从芯片的4个脚分别是 忽略
-->对应S32K1的 PTB14/LPSPI1_SCK(SCLK) PTB15/LPSPI2_SIN(MISO) PTB16/LPSPI2_SOUT(MOSI) PTB17/LPSPI1_PCS0(CS)

3.主芯片的4个脚分别是 忽略
-->对应S32K1的 PTA26/LPSPI0_PCS0(CS) PTA27/LPSPI0_SOUT(SDO) PTD15/LPSPI0_SCK(SCLK) PTD16/LPSPI0_SIN(SDI)
另外还有2个引脚用来辅助业务的作业

3.2 SPI配置

3.2.1 SpiChannelType

这个东西决定SPI通道的类型
1.EB 外部缓冲器
2.IB 内部缓冲器
外部提供的缓冲要更大一些

3.2.2 SpiTransferStart

高位开始还是低位开始
LSB:least significant bit 表示二进制数据的最低位
比如一个数字 10 = 0xA = 1010
传输时就是 0101过去(先发低位)

MSB : most significant bit 表示二进制数据的最高位
比如一个数字 10 = 0xA = 1010
传输时就是 1010过去(先发高位)

3.2.3 SpiBaudrate

SPI波特率
波特率代表每秒钟传输的比特位 设置的值不会弄乱从设备就行

3.2.4 SpiCsSelection

SPI CS脚的默认状态初始化
设置由硬件自动初始化还是驱动手动初始化

3.2.5 SpiDataShiftEdge

何时进行数据采用/发送
Leading和Trailing的出现时机和极性和相位是没有关系的
1.Trailing -- 时钟变到非初始状态的那个沿 -- 意味数据更新
2.Leading -- 时钟变回初始状态的那个沿 -- 意味bit发送完毕
3.Trailing Edge 总是在 Leading Edge 之前出现
4.相位和极性的4种组合只会影响当选择二者之一采样时数据是0->1还是1->0.
5.采样如果选leading那么其实和下降沿采样还是上升沿采样没关系,其实是在时钟返回初始状态时采样.
此时再根据极性判断是下降沿还是上升沿.根据相位判断是0还是1

3.2.6 SpiTimeClk2Cs

tASC(Time from CS to SCK) -- 在主设备拉低时钟线后,给片选信号(CS)一个延迟(TASC规定时间),以确保时钟线的状态是稳定的且是主设备控制的.

3.2.7 SpiTimeCs2Clk

经历TASC后CS被成功拉动 那么此时要准备发送数据 .从CS被拉动完毕到第一个数据发送出来的时间间隔叫tCSC (Time from CS to SCK Change).

3.2.8 SpiTimeCs2Cs

当成功等待tCSC发出第一个逻辑电平状态后,需要维持住tDT时间来满足约定的规范 不然可能被其他设备认为是无效消息.

3.2.9 SpiCsContinous

在数据传输完毕前CS线会不会释放.

3.3时钟配置

1.本项目用8MHz频率的SIRC时钟作为输入
2.S32K-RM.pdf中指出LPSPI的时钟源若是SIRC则必须用SIRCDIV2_CLK作为输入
3.SIRCDIV2_CLK被配置为分频一半即4MHz
4.MCU模块的McuPeripheralClockConfig中添加一条SPI的即可
5.SPI模块的SpiPhyUnit时钟使用第4步生成的提供

4.RTD demo学习使用

demo: EB\plugins\Spi_TS_T40D2M10I1R0\examples\S32DS\Spi_Transfer_S32K148\

4.1概念/定义理解

需要尝试搞懂一些驱动的设计思路

4.1.1 Sequence(时序/序列/流)

sequence: 当驱动的上层触发调用了驱动接口.那么驱动层的一条执行流就是一条seq.
比如驱动的上层预期进行一次Transmit,那么从Spi.c提供的接口进入到最后配置寄存器后Fifo的发出.这一整条控制流(链,时序,序列)叫sequence
MCAL中配置的seq则是这个seq服务于哪个功能?Master?Slave? 更多的是SPI通道的划分,S32K148提供了3个SPI模组(3个channel)和一些Flexio配SPI.一个channel就用一个Seq形式特定的业务流.坐Master一个seq,Slave一个seq.

4.1.2 Job(工作/作业/任务)

job: 一条seq中会分步骤执行很多关键的节点.引用上面的例子,硬件,软件的配置是否合法?报文的组装,寄存器的配置,这些关键步骤就分为了一个个job来完成
在MCAL中配置的JOB类别并不是各个步骤JOB的细节,而是JOB的管理外壳,规范了JOB的startnotfi和endnotifi.还有JOB所用的硬件资源以及优先级
也就是说JOB是各个seq公用的.各个JOB像是一个个积木,当我想要某个功能时(也就是seq),他们会根据预设的静态代码进行顺序执行.
管理中断业务,函数对重入的规范和SEQ对JOB的抢占,以及Fifo的使用是值得尝试理解的地方.能够对未来软件架构的设计提供帮助还能增强未来代码的健壮性.

4.1.3 驱动代码分层

Lpspi_Ip.c-->它将最底层的寄存器级别的硬件资源抽象成统一接口,比如配置时钟(TASC那些东西),LSB大小端配置 触发FIFO传输,配置帧大小,塞数据进FIFO,DMA中断处理等等.
Spi_Ipw.c -->他将Lpspi_Ip提供的硬件接口按照功能划分抽象成了功能接口,比如传输Job(配置时钟,控制cs,配置传输类的寄存器来触发传输),配置时钟模式(4个模式)
Spi.c -->它将Spi_Ipw提供的功能对外的接口抽象成业务接口.服务于AUTOSAR标准,让其他厂商的上层比如OS,BSW和其他基于AUTOSAR架构的软件模块调用.
SchM_Spi.c -->全局资源的访问需要小心,为了避免出现异常需要开关中断保证资源的独占.而开关中断需要用到ARM内核的寄存器,访问没映射到内存的寄存器只能通过ARM提供的的指令集.而SchM_Spi.c提供的函数正是将相应指令集封装成API.由此在访问全局资源时能够快捷的开关中断.

4.2 MCAL配置结构体

没用DMA和Flexio 就没画相关的
img

4.3 同步传输

没用DMA和Flexio 就没画相关的
收一帧发一帧
img

4.4 异步传输

没用DMA和Flexio 就没画相关的
img

4.5 Demo总结

Demo展示了作为主/从如何发送/接收数据,是一次回环
所有的操作要在Spi_init后
不论是用EB存储还是IB存储都需要提供相应的Tx和Rx的Buffer
同步会在传输接口中直接进行收发.
异步会在传输函数调用先触发传输中断.在触发的中断中进行读取/发送数据.也就是说用户从不需要主动在回调中处理数据,甚至可以不关心收/发中断
异步传输会在中断回调中处理多余的JOB
spi.c\Ipw\IP每层都有自己独立的状态管理
所以如DEMO所见
1.先setup buffer
2.再触发seq的传输流程
3.等待seq返回完成
4.Read buffer

4.6 Demo代码

点击查看代码
/*
*   (c) Copyright 2020 NXP
*
*   NXP Confidential. This software is owned or controlled by NXP and may only be used strictly
*   in accordance with the applicable license terms.  By expressly accepting
*   such terms or by downloading, installing, activating and/or otherwise using
*   the software, you are agreeing that you have read, and that you agree to
*   comply with and are bound by, such license terms.  If you do not agree to
*   be bound by the applicable license terms, then you may not retain,
*   install, activate or otherwise use the software.
*
*   This file contains sample code only. It is not part of the production code deliverables.
*/

#ifdef __cplusplus
extern "C" {
#endif


/*==================================================================================================
*                                        INCLUDE FILES
* 1) system and project includes
* 2) needed interfaces from external units
* 3) internal and external interfaces from this unit
==================================================================================================*/
#include "Mcu.h"
#include "Spi.h"
#include "Platform.h"
#include "Mcl.h"
#include "Port.h"

#include "check_example.h"

/*==================================================================================================
*                          LOCAL TYPEDEFS (STRUCTURES, UNIONS, ENUMS)
==================================================================================================*/


/*==================================================================================================
*                                       LOCAL MACROS
==================================================================================================*/


/*==================================================================================================
*                                      LOCAL CONSTANTS
==================================================================================================*/


/*==================================================================================================
*                                      LOCAL VARIABLES
==================================================================================================*/


/*==================================================================================================
*                                      GLOBAL CONSTANTS
==================================================================================================*/

/*==================================================================================================
*                                      GLOBAL VARIABLES
==================================================================================================*/


/*==================================================================================================
*                                   LOCAL FUNCTION PROTOTYPES
==================================================================================================*/
int main(void);

/*==================================================================================================
*                                       LOCAL FUNCTIONS
==================================================================================================*/


/*==================================================================================================
*                                       GLOBAL FUNCTIONS
==================================================================================================*/
#define SPI_START_SEC_VAR_CLEARED_UNSPECIFIED_NO_CACHEABLE
#include "Spi_MemMap.h"
static Spi_DataBufferType RxChBuf0[20];
static Spi_DataBufferType RxChBuf1[20];
#define SPI_STOP_SEC_VAR_CLEARED_UNSPECIFIED_NO_CACHEABLE
#include "Spi_MemMap.h"

#define SPI_START_SEC_VAR_INIT_UNSPECIFIED_NO_CACHEABLE
#include "Spi_MemMap.h"
static Spi_DataBufferType TxChBuf0[20] = {0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,20};
static Spi_DataBufferType TxChBuf1[20] = {0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30};
#define SPI_STOP_SEC_VAR_INIT_UNSPECIFIED_NO_CACHEABLE
#include "Spi_MemMap.h"

/**
* @brief        Main function of the example
*/
int main(void)
{
    boolean Result = TRUE;
    uint8 Index;

    /* Initialize the Mcu driver */
    Mcu_Init(NULL_PTR);
    Mcu_InitClock(McuClockSettingConfig_0);
#if (MCU_NO_PLL == STD_OFF)
    while ( MCU_PLL_LOCKED != Mcu_GetPllStatus() )
    {
        /* Busy wait until the System PLL is locked */
    }

    Mcu_DistributePllClock();
#endif
    Mcu_SetMode(McuModeSettingConf_0);

    /* Initialize Port driver */
    Port_Init(NULL_PTR);
    /* Initialize Interrupt */
    Platform_Init(NULL_PTR);
    /* Initialize Mcl for using Flexio and Dma*/
    Mcl_Init(NULL_PTR);

    Spi_Init(NULL_PTR);

    /* Using interrupt in transfer */
    Spi_SetAsyncMode(SPI_INTERRUPT_MODE);

    /* Set up external buffer to transmission and reception */
    Spi_SetupEB(0u, TxChBuf0, RxChBuf0, 20u);

    /* Copy transmitted data to internal buffer */
    Spi_WriteIB(1u, TxChBuf1);

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

    /* This sequence of master transferring 10 frame 16 bits using synchronous method*/
    Spi_SyncTransmit(SpiConf_SpiSequence_SpiSequence_Master);

    while (SPI_SEQ_OK != Spi_GetSequenceResult(SpiConf_SpiSequence_SpiSequence_Slave));

    /* Read received buffer with data length configured in configuration tool */
    Spi_ReadIB(1u, RxChBuf1);

    for (Index = 0u; Index < 20u; Index++)
    {
        if (TxChBuf0[Index] != RxChBuf1[Index])
        {
            Result = FALSE;
            break;
        }
        if (TxChBuf1[Index] != RxChBuf0[Index])
        {
            Result = FALSE;
            break;
        }
    }

    Exit_Example(Result);

    return (0U);
}


#ifdef __cplusplus
}
#endif

/** @} */

posted @ 2024-10-16 17:11  fhz2000  阅读(863)  评论(0)    收藏  举报