汽车电子expert成长之路

本博客发布的个人原创精品----嵌入式系统技术文章,欢迎大家参考学习,并转发分享!

S32K SDK使用详解之S32 SDK软件架构详解

0
阅读(16412)

内容提要

引言

1. SDK的MCU平台相关设备驱动解析(SDK-->platform-->devices目录)

    1.1 子目录common

    1.2 子目录S32K1xx(为具体使用的MCU型号,可能为 S32K116/S32K118/S31K142/S32K144/S32K146/S32K148)

    1.3 其他文件

2. SDK的MCU平台相关PD外设驱动解析

3. SDK的MCU平台相关PAL外设抽象层驱动解析

4. SDK的中间件(Middleware)解析

5. SDK的操作系统(OS)解析

    5.1 Source-->include

    5.2 Source-->portable

    5.3 其他源文件

    5.4 rtos-->osif

总结


    引言


    为了帮助大家更好的学习和使用S32K SDK,从本文开始,我将开发一系列的《S32K SDK使用详解》技术文章,介绍S32K SDK的软件架构和编程思想,并结合S32K1xx系列MCU的硬件外设模块功能特性(Freatures),介绍S32K SDK中相应PD/PAL组件、FreeRTOS/Osif组件和TSS,TCP/IP/LIN stack中间件(Middleware)组件的Processor Expert配置和API使用细节和Tips等。

今天,先解析S32K SDK的软件架构,让大家对S32K SDK的软件架构有一个整体认识和了解。

4.jpg


      1. SDK的MCU平台相关设备驱动解析(SDK-->platform-->devices目录)

        在SDK-->platform-->devices目录下,有如下子目录和C源文件:

8.jpg


    1.1 子目录common


  • s32_core_cm4.h:内核相关的操作定义,比如全局中断的使能与关闭,进入低功耗等需要汇编指令操作的宏定义,这些汇编指令往往还与使用的编译器原语(directive,也称作编译程式定向 )和#pragma及section定义有关。

 

        用户在应用程序中可以使用C语言直接调用这些宏定义,而无需关心具体的编译器和汇编指令;

 

    其中,比较重要和常用的宏定义包括:

  1. CPU全局中断使能-- ENABLE_INTERRUPTS();

  2. CPU全局中断关闭--DISABLE_INTERRUPTS();

9.jpg


           这两个宏定义被SDK的中断管理器(interrupt manager)组件的全局API--INT_SYS_EnableIRQGlobal()和INT_SYS_DisableIRQGlobal()调用:


    Tips:推荐大家调用SDK中断管理器(interrupt manager)组件的全局API--INT_SYS_EnableIRQGlobal()和INT_SYS_DisableIRQGlobal(),而不是直接使用以上两个宏定义,因为INT_SYS_EnableIRQGlobal()和INT_SYS_DisableIRQGlobal()中加入了中断嵌套计数,可以跟踪调试中断嵌套。

    10.jpg


    1. 进入低功耗模式指令--STANDBY();


      11.jpg

    该宏定义也无需用户自己调用,而是被SDK的电源管理器(power manager)组件的硬件访问层(hw_access)的API--SMC_SetPowerMode()调用,要进入低功耗模式,还需要配置PMC(Power Mode Controller,电源模式控制器)、SMC(System Mode Controller, 系统模式控制器)模块和SCB(System Control Block,系统控制块,属于ARM Cortex M内核的系统配置,具体请参考ARM Cortex M系列内核手册)等模块,并配置系统和模块时钟才可以成功进入低功耗模式。

           SMC_SetPowerMode()又被驱动层的POWER_SYS_SwitchToRunningPowerMode()调用,最后再被SDK的电源管理器(power manager)组件的API--POWER_SYS_SetMode()调用:

    12.jpg

           因此,用户在应用程序中只需要调用SDK的电源管理器(power manager)组件的API--POWER_SYS_SetMode()即可。


    1. 代码重映射到SRAM中(.code_ram段)的宏定义:

    • START_FUNCTION_DECLARATION_RAMSECTION

    • END_FUNCTION_DECLARATION_RAMSECTION

    13.jpg

           使用时,只需要将想要重映射的代码函数申明放在这两个宏定义之间即可,比如SDK的Flash PD外设驱动中的FLASH_DRV_CommandSequence()定义到SRAM中,从而避免Flash擦除和编程的RWW(Read-While-Write)冲突:

    10.jpg

            Tips:注意,使用这两个宏定义申明重映射函数时,不能有分号“;”。


            1.2 子目录S32K1xx(为具体使用的MCU型号,可能为 S32K116/S32K118/S31K142/S32K144/S32K146/S32K148)

         

        其中又包含两个子目录:


            Include子目录


    • S32K148_features.h:定义芯片级的外设模块特性;其中包含了当前版本SDK所支持的芯片勘误表(Errata)信息,若此头文件中定义了某一Errata编号,则SDK中将使能并包含该错误的修正(workaround)程序:

    11.jpg

        Tips: NXP汽车级MCU的勘误表,其中列出了具体掩码集(maskset, 芯片背面第二行有数字和字母组成的5位编码)的芯片已知的bug描述和workaround,对使用芯片的工程师来说十分重要,一定要仔细阅读。请到产品官方网站下载。比如S32K1xx系列的MCU的勘误表可到如下网页链接下载:

    https://www.nxp.com/products/processors-and-microcontrollers/arm-based-processors-and-mcus/s32-automotive-platform/32-bit-automotive-general-purpose-microcontrollers:S32K?tab=Documentation_Tab;

    10.jpg


    • S32K148.h:定义芯片外设模块硬件访问层寄存器;


    Startup子目录


    • system_S32K148.c:实现ARM Cortex M系列MCU的软件接口标准(CMSIS-)规定的系统和时钟初始化API函数,主要包括:


    1. SystemInit():系统初始化函数,其中对FPU、Watchdog、CPU内核指令缓冲(I-Cache)以及MPU的使能和初始化,在SDK的启动过程中调用;

    11.jpg



    1. SystemCoreClockUpdate():系统内核时钟更新函数,在S32K1xx的SDK中未调用;

    2. SystemSoftwareReset():系统软件复位函数,通过配置内核的SCB模块实现为MCU的软件复位;

    • system_S32K148.h:system_S32K148.c:实现的系统配置API函数的申明和其中对Watchdog、CPU内核I-Cache以及MPU、系统时钟和外部晶振频率的配置宏定义:

    11.jpg


            Tips:如果调用了SystemCoreClockUpdate()对MCU时钟进行初始化,则可以通过全局变量SystemCoreClock获得MCU的内核时钟(System Core Clock),同时也是内核系统心跳时钟(System Tick Timer)频率,单位为Hz。

     

        1.3 其他文件


    • callbacks.h:其中主要定义了S32K1xx系列MCU的外设中断事件(event)类型和中断回调函数(callback)类型定义,使用C语言关键词typedef将:

    • 每一类外设的中断事件类型定义为:<Peripernal_Name>_event_t的枚举类型,


    • 每一类外设的中断回调函数类型定义为:*<Peripernal_Name>_callback_t的函数指针;


              比如,以下为其中SPI和UART的中断事件类型和中断回调函数类型的定义:

      11.jpg


              中断事件类型是外设中断回调函数(callback)的关键参数,为用户中断回调函数处理提供关键信息。

       

              Tips:S32 SDK中为大部分外设都提供了中断处理函数(ISR)—位于外设驱动PD的中断处理文件(<Peripheral_Name>_irq.c/h)中,并在外设初始化时配置了相应的中断,其外设中断ISR属于SDK PD层驱动非常重要的一部分代码,不允许用户修改,因此提供了在外设中断ISR中调用用户中断回调函数的方式给用户提供中断处理接口。

       

              Tips:在用户中断回调函数中,无需用户自己清除外设中断标志,因为在SDK提供的外设中断ISR中已经做了相应的处理。只有需要用户调用SDK中断管理器(interrupt_manager)的API--INT_SYS_InstallHandler()手动注册安装的外设中断ISR,才需要用户自己清外设中断标志,比如典型的GPIO的IRQ中断和PIT定时器中断;


      • devassert.h:在此文件中主要定义了SDK使用的断言(assert)函数,其广泛使用于SDK的API函数进行函数参数范围和合法性检查:

      12.jpg

              由以上代码可知,通过宏定义CUSTOM_DEVASSERT和DEV_ERROR_DETECT来选择使用用户自定义的断言函数还是使用SDK定义的错误检查断言函数;默认用户自定义断言函数为空,用户可以自行添加自己的断言处理函数;SDK使用的断言函数处理是如果发生错误,则进入死循环等待;默认CUSTOM_DEVASSERT和DEV_ERROR_DETECT都没有定义,所以也是使用的一个空函数,即未做任何处理。


          Tips:用户若要使用自定义或者SDK的断言函数,可以将通过设置应用工程属性在C编译器的预处理符号定义(C/C++ Build-->Settings-->Tool Settings-->Standard S32DS C Compiler-->Preprocessor-->Defined system)中添加CUSTOM_DEVASSERT或者DEV_ERROR_DETECT即可,比如如下为使能DEV_ERROR_DETECT的设置:

      13.jpg


              按以上设置之后,SDK的断言处理函数就被使能了,clean工程重新编译即可生效:

      11.jpg

      • device_registers.h:此头文件中根据应用工程属性中C编译器的预处理器符号定义中的MCU型号,包含相应的外设寄存器定义(S32K1xx.h)和芯片级外设特性定义头文件(S32K1xx_features.h)以及MCU内核操作API头文件(S32_core_cm4/cm0.h)断言函数申明头文件(devassert.h);

      12.jpg

      • startup.c/h:定义SDK的启动代码中调用的SRAM初始化函数init_data_bss ():

      13.jpg


              其利用链接文件中定义的段标识符和默认段链接结果,对MCU的SRAM进行初始化,主要包括以下初始化工作:


      1. 初始化.data段,将全局变量的初始化值从Flash中拷贝到SRAM;

      2. 初始化.code_ram段,将重映射到SRAM中的程序代码从Flash中拷贝到SRAM;

      3. 初始化.bss段,将.bss段所在的SRAM区清零;

      4. 初始化.custom_ram段,将放在用户自定义段中的有初始化值的全局变量的初始化值从Flash拷贝到SRAM中;

      5. 将外设中断向量表从Flash拷贝到SRAM并将新的中断向量表地址初始化给CPU内核中断向量基地址寄存器;


              Tips:所有SRAM段的初始化都是先判断其长度是否为零(即是否实际使用了),只有长度不为零时才对其进行初始化;

      是否将中断向量表拷贝到SRAM中,取决于__VECTOR_RAM是否等于__VECTOR_TABLE,这两个符号在工程的链接文件中定义,对于调试目标为Debug和Release的编译,使用的是S32K1xx_xx_flash.ld,其中__VECTOR_RAM不等于__VECTOR_TABLE,故要进行中断向量表的拷贝;而调试目标为Debug_RAM的编译,使用的是S32K1xx_xx_ram.ld,其中__VECTOR_RAM等于__VECTOR_TABLE,故无需进行中断向量表的拷贝;因为前者的编译结果是放在Flash中的,不支持SDK中使用中断管理器的API--INT_SYS_InstallHandler(),在程序运行时注册/修改外设中断向量;

       

      • status.h:其中主要以枚举类型的方式定义了SDK中所有API会使用到的返回代码status_t:

      11.jpg


              Tips:这些返回编码也可以在用户的应用程序中使用,只需要包含status.h这个头文件即可。

       

              2. SDK的MCU平台相关PD外设驱动解析


              PD(Peripheral Driver)外设驱动代码位于SDK-->platform-->drivers-->src目录下,每一个外设(比如LPUART、FlexCAN、EDMA或者FTM--FlexTimer)一个子目录,其中一般包括:


      • <Peripheral_Name>_driver.c : 外设驱动API函数定义与实现;

      • <Peripheral_Name>hw_access.c/h : 外设硬件(HardWare)寄存器访问函数和头文件;

      • <Peripheral_Name>_irq.c/h: 外设驱动中断ISR函数和头文件;

      12.jpg


              相应的外设驱动API包含在SDK-->platform-->drivers-->inc目录下,每一个外设(比如LPUART、FlexCAN、EDMA或者FTM--FlexTimer)一个对应的头文件:

      13.jpg


              Tips:部分SDK的PD外设驱动所包含的硬件访问层(hw_access)API函数也是全局的,可以在用户应用程序中调用,以完成对外设寄存器的特殊要求配置;


            3. SDK的MCU平台相关PAL外设抽象层驱动解析


              PAL(Peripheral Abstract Layer)外设抽象层驱动代码位于SDK-->platform-->pal目录下,实现某一功能的外设抽象集(比如CAN、I2S、UART、timer)一个子目录,其子目录又包含inc和src两个子目录:

      其中inc目录包含如下文件


      • <PAL_Name>_pal_mapping.h : 外设抽象层驱动与PD的映射;

      • <PAL_Name>_pal.h : 外设抽象层驱动API函数申明的头文件;


          而src目录下包含如下文件:


      • <PAL_Name>_pal.c: 外设抽象层驱动API函数定义与实现—封装调用映射的底层PD外设驱动API;

      • <PAL_Name>_irq.c/h: 中断ISR实现和头文件,timing PAL组件特有的,实现定时器相关的功能(比如IC—输入捕捉、OC—输出比较、PWM—脉宽调制信号产生 )中断ISR,其他外设的中断处理在PD外设驱动层完成,在PAL没有中断相关处理程序。

      14.jpg


            4. SDK的中间件(Middleware)解析


              在S32 SDK中提供了众多的中间件组件,比如SBC芯片(UJA1169/UJA113x)的驱动、电极触摸感应库(TSS)、ISELED驱动、NFC协议栈、LIN通信协议栈和TCP/IP协议栈等,其位于目录SDK-->middleware下。


              每个中间件组件有一个自己的子目录,并包含include和source两个子目录,分别存放API函数申明头文件和API定义/封装函数源文件:

      15.jpg


              SDK的中间件通过处理器专家来图形化界面配置并自动生成代码。添加SDK的中间件到应用工程时,将自动添加其依赖的SDK外设组件,并且通过中间件组件的处理器专家图形化配置界面即可完成相应外设组件的配置:


              比如如下添加的TSS库,其调用了PD外设驱动层的LPTMR和ADC组件,也被同时添加到工程中了:

      16.jpg        需要注意的是:在S32K1xx SDK中中间件TSS(Touch Sensing)、ISELED和NFCStack都是以静态库(xxx.a)的方式提供的,用户需要包含相应的include中的头文件,即可调用其提供的API函数实现相应的功能,但无法查看其C语言程序源代码。

              

              添加这些中间件组件到应用工程时,还会将相应的静态库添加到工程的链接器选项设置中,比如如下为添加TSS库后的结果:

      17.jpg


            5. SDK的操作系统(OS)解析


              随着汽车电子电控系统的日益复杂,汽车ECU对功能安全(Function Safety)、信息安全(Information Security)的要求日益增加,车用MCU的CPU工作频率和存储器资源以及外设资源也相应增多,在MCU中集成的功能任务也不断增加,以往裸写代码(bare metal code )的方式进行软件程序开发来保证众多任务的实时性也越来越面临挑战,因此嵌入式实时操作系统(RTOS—Real-Time  Operation System)越来越多的被应用于汽车ECU的开发中。


              S32K1xx系列MCU虽然面向的是通用汽车电子应用,但其集成了多达3路FlexCAN,且可以全部支持CAN-FD,在32K148中甚至还集成了1路10M/100M以太网控制器(ENET MAC),在存储器资源方面,SRAM为17KB~256KB,Flash为128KB~2MB,CPU内核也可以运行到最高112MHz(S32K14x系列,HSRUN模式):

      17.jpg

              因此,在S32K1xx SDK中也提供了两个操作系统(OS)组件—FreeRTOS和osif,以帮助用户开发应用程序,进行存储器/外设资源管理和任务调度。其源代码为SDK-->rtos目录下:

      17.jpg

             其中FreeRTOS_S32K子目录下存放FreeRTOS移植到S32K1xx系列MCU的相关的源代码:


              5.1 Source-->include

              包含FreeRTOS的内核(Kernel)操作头文件。


      • croutine.h:多核协同任务进程(co-routine)支持任务调度和管理API函数申明头文件,S32K1xx为单核MCU,故未使用;

      • deprecated_definitions.h:具体MCU平台移植(Platform Porting)宏定义头文件,早期版本的FreeRTOS使用,保留在此是为了兼容老版本的FreeRTOS,在S32K1xx中未使用;

      • event_groups.h:FreeRTOS的中断事件(Event)管理数据类型和API函数定义头文件;

      • FreeRTOS.h:包含用于FreeRTOS移植的通用头文件;

      • list.h:用于内核调度器(Scheduler)的列表操作数据结构和API函数定义与申明,当然这个数据结构也可被应用程序代码调用;

      • mpu_wrappers.h:包含用于带MPU的MCU移植FreeRTOS的重定义的API函数,通过封装宏(wrapper macro)调用;

      • portable.h:可移植层API申明头文件,其中的每一个API函数都必须在移植时定义和实现;

      • projdefs.h:包含portable.h中会用到的很多宏定义,比如ms到OS系统心跳定时器的tick转换,FreeRTOS错误定义以及用于FreeRTOS+ components的错误代码和大小端选择宏定义等;

      • queue.h:包含FreeRTOS中队列(queue)操作和管理的数据类型和宏定义以及API函数申明;

      • semphr.h:包含FreeRTOS中信号量(Semaphores)操作和管理的数据类型和宏定义以及API函数申明;

      • StackMacros.h:定义用于堆栈溢出(stack overflow)检查的钩子函数(hook function);

      • task.h:包含list.h,并定义FreeRTOS内核任务调度和管理使用的数据结构和API函数申明,包括像任务创建xTaskCreate();任何延迟vTaskDelay()、任务优先级设置vTaskPrioritySet()、任务挂起vTaskSuspend()、任务恢复vTaskResume()等重要的API函数;

      • timers.h:包含FreeRTOS的内核软件定时器(Software Timer)操作和管理相关的数据类型、宏定义和API函数申明;


              5.2 Source-->portable


              存放FreeRTOS MCU平台移植相关的源文件,在S32K1xx的SDK中已经进行了移植,无需用户修改。

      18.jpg

      • GCC-->ARM_CM4F:包含GCC工具链(S32DS IDE 使用gcc compiler和LD linker)下S32K14x的CPU内核ARM Cortex M4F的FreeRTOS移植实现代码,若为S32K11x,则此处为ARM_CM0。注意:在S32K1xx的SDK RTM2.0不支持S32K11x系列MCU,从SDK Beta 2.9.0开始才可以支持S32K11x系列MCU:

      • port.c:在头文件portable.h中定义的所有CPU内核移植的API函数具体实现代码;

      • portmacro.h:与S32K1xx系列MCU硬件相关的内核移植宏定义,包括数据类型定义、内核架构相关的堆栈生长方式(portSTACK_GROWTH)、内核心跳周期(portTICK_PERIOD_MS)、存储器字节对齐(portBYTE_ALIGNMENT)、内核/外设中断使能/关闭/优先级配置API等;

       

      • GCC--> MemMang:包含GCC工具链下FreeRTOS的存储器管理相关移植代码。

      • heap_2.c:FreeRTOS对内存堆(heap)的管理数据类型定义和API函数实现,主要包括堆初始化函数prvHeapInit()、动态内存分配函数pvPortMalloc()、动态内存释放函数vPortFree()等。

       

              Tips:关于FreeRTOS的使用,请访FreeRTOS的官方网站获取更多详细信息和学习资料:


              https://www.freertos.org/index.html;

       

              5.3 其他源文件

       

      • croutine.c:croutine.h头文件中定义的多核协同任务进程(co-routine)支持任务调度和管理API函数实现;

      • event_groups.c:event_groups.h头文件中定义的FreeRTOS中断事件(Event)管理数据类型和API函数实现;

      • list.c:list.h头文件中定义的用于内核调度器(Scheduler)的列表操作的API函数实现;

      • queue.c:queue.h头文件中定义的FreeRTOS中队列(queue)操作和管理的API函数实现;

      • tasks.c:tasks.h头文件中定义的FreeRTOS内核任务调度和管理使用的API函数实现;

      • timers.c:timers.h头文件中定义的FreeRTOS的内核软件定时器(Software Timer)操作和管理相关API函数实现;

       

              5.4 rtos-->osif

          

              其中存放操作系统接口相关的API定义代码。


      • osif_freertos.c:定义基于FreeRTOS的操作系统API接口函数,比如互斥信号(Mutex)量创建/销毁/锁定/解锁和信号量(Semaphore )的创建/销毁/发送/等待以及任务延时等API函数:

        19.jpg


      • osif.h:操作系统接口API函数的申明头文件

      • osif_baremetal.c:裸写代码时的操作系统API接口函数定义,其中也实现了以上API函数,基于ARM Cortex M内核System Tick Timer;

      20.jpg


              Tips:若添加了SDK的FreeRTOS组件,则rtos-->osif使用osif_freertos.c,否则使用osif_baremetal.c。操作系统接口(osif)组件提供的互斥信号量和信号量操作以及任务延时API函数常被S32K1xx SDK中的各种通信外设组件调用,以完成对通信收发数据的同步操作;因此其会在添加此类通信外设组件时,自动添加到应用工程中。

       

              总结


              本文只是抛砖引玉,后续还有更多《S32K SDK使用详解》系列文章干货分享,敬请关注~!