S32K144EVB的学习历程(一)
前言本文主要介绍了本人在学习使用S32K144EVB中遇到的问题和解决办法,由于本芯片是NXP(原 freescale) 生产的基于 ARM M4F 内核的32位芯片,主要适用对象是汽车 。目前在网络上该芯片还没有相关的中文学习资料,到笔者写本文目前,网络上能够找到的资料只有开发板的电路图和 Reference Manual 和该芯片配套的 IDE 内置头文件以及给出的 cookbook 例程,笔者也是在一步步摸索学习,故本文为一个记录性质的文章。本文阅读需要 C 语言基础和一些简单的单片机知识,笔者在之前曾经开发过51单片机和 freescale 公司的 HC08GP32 单片机,故可能会跳过一些基础说明。由于该芯片的 Manual 文件长达 1929 页,全读完肯定要浪费很多时间,为了节约时间,我就针对例程中给出的部分内容查询手册相关内容,进行分析。本文针对 S32K144EVB-Q100X 开发板,但基本原理都是相同的。Hello World1. 本例程主要包含以下部分的操作:配置 GPIO根据按键状态输出 LED 灯信号2. 使用到的电路图:
Hello World block diagram3. 第一个例程的代码如下:#include "S32K144.h" /* include peripheral declarations S32K144 */#define PTD0 0 /* Port PTD0, bit 0: FRDM EVB output to blue LED */#define PTC12 12 /* Port PTC12, bit 12: FRDM EVB input from BTN0 [SW2] */void WDOG_disable (void){WDOG->CNT=0xD928C520; /*Unlock watchdog*/WDOG->TOVAL=0x0000FFFF; /*Maximum timeout value*/WDOG->CS = 0x00002100; /*Disable watchdog*/}int main(void) {int counter = 0;WDOG_disable();/* Enable clocks to peripherals (PORT modules) */PCC-> PCCn[PCC_PORTC_INDEX] = PCC_PCCn_CGC_MASK; /* Enable clock to PORT C */PCC-> PCCn[PCC_PORTD_INDEX] = PCC_PCCn_CGC_MASK; /* Enable clock to PORT D *//* Configure port C12 as GPIO input (BTN 0 [SW2] on EVB) */PTC->PDDR &= ~(1<<PTC12); /* Port C12: Data Direction= input (default) */PORTC->PCR[12] = 0x00000110; /* Port C12: MUX = GPIO, input filter enabled *//* Configure port D0 as GPIO output (LED on EVB) */PTD->PDDR |= 1<<PTD0; /* Port D0: Data Direction= output */PORTD->PCR[0] = 0x00000100; /* Port D0: MUX = GPIO */for(;;) {if (PTC->PDIR & (1<<PTC12)) { /* If Pad Data Input = 1 (BTN0 [SW2] pushed) */PTD-> PCOR |= 1<<PTD0; /* Clear Output on port D0 (LED on) */}else { /* If BTN0 was not pushed */PTD-> PSOR |= 1<<PTD0; /* Set Output on port D0 (LED off) */}counter++;}4. 代码详解主要关注 main() 内部PCC-> PCCn[PCC_PORTC_INDEX] = PCC_PCCn_CGC_MASK; /* Enable clock to PORT C */PCC-> PCCn[PCC_PORTD_INDEX] = PCC_PCCn_CGC_MASK; /* Enable clock to PORT D */这两句话使用的 PCC 等变量名都是在头文件 "S32K144.h" 中定义的:/** PCC - Size of Registers Arrays */#define PCC_PCCn_COUNT 116u/** PCC - Register Layout Typedef */typedef struct { __IO uint32_t PCCn[PCC_PCCn_COUNT]; /**< PCC Reserved Register 0..PCC CMP0 Register, array offset: 0x0, array step: 0x4 */} PCC_Type, *PCC_MemMapPtr;/** Peripheral PCC base address */#define PCC_BASE (0x40065000u)/** Peripheral PCC base pointer */#define PCC ((PCC_Type *)PCC_BASE)PCC 是一个指向固定地址的 PCC_Type结构体指针,他的固定地址是 (0x40065000u) 它对应的 PCC_Type 结构拥有一个116个无符号整型变量的数组 PCCn根据注释内容判断,这个指针的主要作用是用来改变 PCC (Peripheral Clock Controller)控制器内部寄存器的值(下称 PCC ),PCC 控制有关外部时钟频率相关的设置。查询了 Reference Manual 后得知,PCC 有三个功能:时钟界面开闭控制 CGC (Clock Gating Controller)*功能性时钟源选择控制(如果对应模块有时钟源)*功能性时钟分频值控制(如果对应模块有分频器)在这个地方,我们仅仅用到第一个功能,也就是时钟界面开关功能。在本文文末,我将给出 PCC 的内存地图。PCC 模块给芯片上面每一个外围模块都设置了独自的 PCC 内部寄存器地址,用于控制以上的三个功能,PCC 内的每一个寄存器都有一个时钟界面开闭位 (CGC)。在每一个模块使用前,必须打开该模块的CGC (CGC = 1),才能使用该模块如何打开?首先是寻址,在头文件 "S32K144.h" 中已经将 PCC 控制器的各个寄存器地址全部用宏定义了:/* PCC index offsets */...#define PCC_PORTA_INDEX 73#define PCC_PORTB_INDEX 74#define PCC_PORTC_INDEX 75#define PCC_PORTD_INDEX 76#define PCC_PORTE_INDEX 77...可以看到 GPIO A/B/C/D/E 对应的地址。将其赋值为 PCC_PCCn_CGC_MASK 即可打开 CGC。PCC_PCCn_CGC_MASK 在头文件中定义为:#define PCC_PCCn_CGC_MASK 0x40000000u后面的 GPIO 端口方向控制类似 PCC 的控制,在这里使用了一个 PTC 和 PTD 指针,指向两个固定地址的结构体 GPIO_Type。/** GPIO - Register Layout Typedef */typedef struct { __IO uint32_t PDOR; /**< Port Data Output Register, offset: 0x0 */ __O uint32_t PSOR; /**< Port Set Output Register, offset: 0x4 */ __O uint32_t PCOR; /**< Port Clear Output Register, offset: 0x8 */ __O uint32_t PTOR; /**< Port Toggle Output Register, offset: 0xC */ __I uint32_t PDIR; /**< Port Data Input Register, offset: 0x10 */ __IO uint32_t PDDR; /**< Port Data Direction Register, offset: 0x14 */ __IO uint32_t PIDR; /**< Port Input Disable Register, offset: 0x18 */} GPIO_Type, *GPIO_MemMapPtr;/** Peripheral PTC base address */#define PTC_BASE (0x400FF080u)/** Peripheral PTC base pointer */#define PTC ((GPIO_Type *)PTC_BASE)/** Peripheral PTD base address */#define PTD_BASE (0x400FF0C0u)/** Peripheral PTD base pointer */#define PTD ((GPIO_Type *)PTD_BASE)GPIO 的控制器:NameWidth(in bits)AccessPort Data Output Register (PDOR)32RWPort Set Output Register (PSOR)32WPort Clear Output Register (PCOR)32WPort Toggle Output Register (PTOR)32WPort Data Input Register (PDIR)32RPort Data Direction Register (PDDR)32RWPort Input Disable Register (PIDR)32RWPort Data Output Register (PDOR)FieldNameDescription-PDOPort Data Output输出管脚的值,对应逻辑值Port Set Output Register (PSOR)FieldNameDescription-PTSOPort Set Output将指定管脚的值置 1读取恒为零Port Clear Output Register (PCOR)FieldNameDescription-PTCOPort Clear Output将指定管脚的值置 0读取恒为零Port Toggle Output Register (PTOR)FieldNameDescription-PTTOPort Toggle Output将指定管脚的值反转读取恒为零Port Data Input Register (PDIR)FieldNameDescription-PDIPort Data Input读取指定管脚的值Port Data Direction Register (PDDR)FieldNameDescription-PDDPort Data Direction0 Input1 OutputPort Input Disable Register (PIDR)FieldNameDescription-PIDPort Input Disable0 管脚正常输入1 管脚不能输入端口功能控制 PORT Controller Register我做个比喻,在 ARM 中,各个管脚就像是一个个等待工作的银行柜台窗口,可以存钱,也可以取钱,也可以借贷款,也可以办理理财业务,银行不能一个业务开一个窗口,所以每个窗口必须可以做很多事情,ARM 也是这样,在有限的管脚上,需要进行中断,PWM,GPIO,UART串口,SPI,I2C,CAN 信息交流功能,所以有些管脚有很多功能可以选择,我们要使用某个功能就要自己进行设置,设置的地方呢就在 PCR(Pin Controller Register) 这个寄存器里面。/** PORT - Register Layout Typedef */typedef struct { __IO uint32_t PCR[PORT_PCR_COUNT]; /**< Pin Control Register n, array offset: 0x0, array step: 0x4 */ __O uint32_t GPCLR; /**< Global Pin Control Low Register, offset: 0x80 */ __O uint32_t GPCHR; /**< Global Pin Control High Register, offset: 0x84 */ uint8_t RESERVED_0[24]; __IO uint32_t ISFR; /**< Interrupt Status Flag Register, offset: 0xA0 */ uint8_t RESERVED_1[28]; __IO uint32_t DFER; /**< Digital Filter Enable Register, offset: 0xC0 */ __IO uint32_t DFCR; /**< Digital Filter Clock Register, offset: 0xC4 */ __IO uint32_t DFWR; /**< Digital Filter Width Register, offset: 0xC8 */} PORT_Type, *PORT_MemMapPtr;/** Peripheral PORTC base address */#define PORTC_BASE (0x4004B000u)/** Peripheral PORTC base pointer */#define PORTC ((PORT_Type *)PORTC_BASE)/** Peripheral PORTD base address */#define PORTD_BASE (0x4004C000u)/** Peripheral PORTD base pointer */#define PORTD ((PORT_Type *)PORTD_BASE)同样每个 PCR 都有 32 位,与之前不同的是,这 32 位仅仅设置了一个管脚,而不是 32 个个,这 32 位的功能如下:FieldNameDescription24ISFInterrupt Status Flag0 管脚未检测中断1 管脚检测到中断19-16IRQCInterrupt Configuration 对应管脚的设置如下0000 ISF 关闭0001 ISF标志 和 DMA 请求,产生在上升沿0010 ISF标志 和 DMA 请求,产生在下降沿0011 ISF标志 和 DMA 请求,既在上升沿也在下降沿产生0100 保留0101 保留0110 保留0111 保留1000 SF 标志和中断,产生于逻辑 01001 ISF 标志和中断,产生于上升沿1010 ISF 标志和中断,产生于下降沿1100 ISF 标志和中断,产生于两个沿1100 ISF 标志和中断,产生于逻辑 11101 保留1110 保留1111 保留15LKLock Register0 PCR 寄存器 0 到 15 位值不锁定1 PCR 寄存器 0 - 15 位值锁定,直到下次重新启动才能够更改10-8MUXPin Mux Control 管脚复用控制不是所有的管脚都支持管脚复用,若支持,则可以有以下的设置:000 关闭管脚复用001 功能 1 ,GPIO010 功能 2 ,芯片特定功能011 功能 3 ,芯片特定功能100 功能 4 ,芯片特定功能101 功能 5 ,芯片特定功能110 功能 6 ,芯片特定功能111 功能 7 ,芯片特定功能6DSEDrive Strength Enable DSE 驱动力加强设置,此位在各种复用模式下都有效0 低驱动力模式,如果管脚处于输出模式1 高驱动力模式,如果管脚处于输出模式4PFEPassive Filter Enable 被动滤波功能,此位在各复用状态下都有效0 关闭被动滤波1 开启被动滤波,工作在输入状态下,详情参考滤波说明1PEPull Enable PE 使能上下拉电阻0 无内部上下拉电阻1 有上下拉电阻0PSPull Select PE 选择上下拉电阻0 有上拉电阻1 有下拉电阻总结如果要使用某个 GPIO 端口,需要的准备工作是:使用 PCC 指针打开对应的 PCCn[] 对应的CGC ,PCCn是 PCC 所指向的结构体内部的数组,固定地址,包含一共有116个 uint32 类型寄存器,将对应的寄存器赋值为 PCC_PCCn_CGC_MASK 即可打开 CGC = 1 。设置 GPIO 的控制器中的 PDDR 寄存器,用于调整输入/输出方向。此寄存器在一个类型为 GPIO_Type 的结构中,一共有 5 个固定地址的结构,使用 PTA/PTB/PTC/PTD/PTE 访问。设置 PORT.PCR 控制器,关闭中断,MUX 设置成为 001,是否开启被动滤波。使用 PORTA/PORTB/PORTC/PORTD/PORTE 访问。读取对应的 PDIR (输入),或者给 PDOR 赋值 (输出)。使用 PTA/PTB/PTC/PTD/PTE 访问。附录:PCC 各个寄存器地图偏移地址寄存器名称长度/位 (bit)权限重启默认值80hPCC FTFC Register (PCC_FTFC)32RWC000_0000h84hPCC DMAMUX Register (PCC_DMAMUX)32RW8000_0000h90hPCC FlexCAN0 Register (PCC_FlexCAN0)32RW8000_0000h94hPCC FlexCAN1 Register (PCC_FlexCAN1)32RW8000_0000h98hPCC FTM3 Register (PCC_FTM3)32RW8000_0000h9ChPCC ADC1 Register (PCC_ADC1)32RW8000_0000hAChPCC FlexCAN2 Register (PCC_FlexCAN2)32RW8000_0000hB0hPCC LPSPI0 Register (PCC_LPSPI0)32RW8000_0000hB4hPCC LPSPI1 Register (PCC_LPSPI1)32RW8000_0000hB8hPCC LPSPI2 Register (PCC_LPSPI2)32RW8000_0000hC4hPCC PDB1 Register (PCC_PDB1)32RW8000_0000hC8hPCC CRC Register (PCC_CRC)32RW8000_0000hD8hPCC PDB0 Register (PCC_PDB0)32RW8000_0000hDChPCC LPIT Register (PCC_LPIT)32RW8000_0000hE0hPCC FTM0 Register (PCC_FTM0)32RW8000_0000hE4hPCC FTM1 Register (PCC_FTM1)32RW8000_0000hE8hPCC FTM2 Register (PCC_FTM2)32RW8000_0000hEChPCC ADC0 Register (PCC_ADC0)32RW8000_0000hF4hPCC RTC Register (PCC_RTC)32RW8000_0000h100hPCC LPTMR0 Register (PCC_LPTMR0)32RW8000_0000h124hPCC PORTA Register (PCC_PORTA)32RW8000_0000h128hPCC PORTB Register (PCC_PORTB)32RW8000_0000h12ChPCC PORTC Register (PCC_PORTC)32RW8000_0000h130hPCC PORTD Register (PCC_PORTD)32RW8000_0000h134hPCC PORTE Register (PCC_PORTE)32RW8000_0000h150hPCC SAI0 Register (PCC_SAI0)32RW8000_0000h154hPCC SAI1 Register (PCC_SAI1)32RW8000_0000h168hPCC FlexIO Register (PCC_FlexIO)32RW8000_0000h184hPCC EWM Register (PCC_EWM)32RW8000_0000h198hPCC LPI2C0 Register (PCC_LPI2C0)32RW8000_0000h19ChPCC LPI2C1 Register (PCC_LPI2C1)32RW8000_0000h1A8hPCC LPUART0 Register (PCC_LPUART0)32RW8000_0000h1AChPCC LPUART1 Register (PCC_LPUART1)32RW8000_0000h1B0hPCC LPUART2 Register (PCC_LPUART2)32RW8000_0000h1B8hPCC FTM4 Register (PCC_FTM4)32RW8000_0000h1BChPCC FTM5 Register (PCC_FTM5)32RW8000_0000h1C0hPCC FTM6 Register (PCC_FTM6)32RW8000_0000h1C4hPCC FTM7 Register (PCC_FTM7)32RW8000_0000h1CChPCC CMP0 Register (PCC_CMP0)32RW8000_0000h1D8hPCC QSPI Register (PCC_QSPI)32RW8000_0000h1E4hPCC ENET Register (PCC_ENET)32RW8000_0000h