S32K144EVB的学习历程(一)

前言

本文主要介绍了本人在学习使用 S32K144EVB中遇到的问题和解决办法,由于本芯片是 NXP(原 freescale) 生产的基于 ARM M4F 内核的32位芯片,主要适用对象是汽车 。目前在网络上该芯片还没有相关的中文学习资料,到笔者写本文目前,网络上能够找到的资料只有开发板的电路图和 Reference Manual 和该芯片配套的 IDE 内置头文件以及给出的 cookbook 例程,笔者也是在一步步摸索学习,故本文为一个记录性质的文章。

  • 本文阅读需要 C 语言基础和一些简单的单片机知识,笔者在之前曾经开发过51单片机和 freescale 公司的 HC08GP32 单片机,故可能会跳过一些基础说明。
  • 由于该芯片的 Manual 文件长达 1929 页,全读完肯定要浪费很多时间,为了节约时间,我就针对例程中给出的部分内容查询手册相关内容,进行分析。
  • 本文针对 S32K144EVB-Q100X 开发板,但基本原理都是相同的。

Hello World

1. 本例程主要包含以下部分的操作:

  • 配置 GPIO
  • 根据按键状态输出 LED 灯信号

2. 使用到的电路图:

S32K144EVB的学习历程(一)_第1张图片
Hello World block diagram

3. 第一个例程的代码如下:

#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<PCR[12] = 0x00000110; /* Port C12: MUX = GPIO, input filter enabled */
/* Configure port D0 as GPIO output (LED on EVB) */
PTD->PDDR |= 1<PCR[0] = 0x00000100; /* Port D0: MUX = GPIO */
for(;;) {
if (PTC->PDIR & (1< PCOR |= 1< PSOR |= 1<

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 有三个功能:

  1. 时钟界面开闭控制 CGC (Clock Gating Controller)
  2. *功能性时钟源选择控制(如果对应模块有时钟源)
  3. *功能性时钟分频值控制(如果对应模块有分频器)

在这个地方,我们仅仅用到第一个功能,也就是时钟界面开关功能。在本文文末,我将给出 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 的控制器:

Name Width
(in bits)
Access
Port Data Output Register (PDOR) 32 RW
Port Set Output Register (PSOR) 32 W
Port Clear Output Register (PCOR) 32 W
Port Toggle Output Register (PTOR) 32 W
Port Data Input Register (PDIR) 32 R
Port Data Direction Register (PDDR) 32 RW
Port Input Disable Register (PIDR) 32 RW

Port Data Output Register (PDOR)

Field Name Description
- PDO Port Data Output
输出管脚的值,对应逻辑值

Port Set Output Register (PSOR)

Field Name Description
- PTSO Port Set Output
将指定管脚的值置 1
读取恒为零

Port Clear Output Register (PCOR)

Field Name Description
- PTCO Port Clear Output
将指定管脚的值置 0
读取恒为零

Port Toggle Output Register (PTOR)

Field Name Description
- PTTO Port Toggle Output
将指定管脚的值反转
读取恒为零

Port Data Input Register (PDIR)

Field Name Description
- PDI Port Data Input
读取指定管脚的值

Port Data Direction Register (PDDR)

Field Name Description
- PDD Port Data Direction
0 Input
1 Output

Port Input Disable Register (PIDR)

Field Name Description
- PID Port Input Disable
0 管脚正常输入
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 位的功能如下:

Field Name Description
24 ISF Interrupt Status Flag

0 管脚未检测中断
1 管脚检测到中断
19-16 IRQC Interrupt Configuration 对应管脚的设置如下

0000 ISF 关闭
0001 ISF标志 和 DMA 请求,产生在上升沿
0010 ISF标志 和 DMA 请求,产生在下降沿
0011 ISF标志 和 DMA 请求,既在上升沿也在下降沿产生
0100 保留
0101 保留
0110 保留
0111 保留
1000 SF 标志和中断,产生于逻辑 0
1001 ISF 标志和中断,产生于上升沿
1010 ISF 标志和中断,产生于下降沿
1100 ISF 标志和中断,产生于两个沿
1100 ISF 标志和中断,产生于逻辑 1
1101 保留
1110 保留
1111 保留
15 LK Lock Register

0 PCR 寄存器 0 到 15 位值不锁定
1 PCR 寄存器 0 - 15 位值锁定,直到下次重新启动才能够更改
10-8 MUX Pin Mux Control 管脚复用控制

不是所有的管脚都支持管脚复用,若支持,则可以有以下的设置:
000 关闭管脚复用
001 功能 1 ,GPIO
010 功能 2 ,芯片特定功能
011 功能 3 ,芯片特定功能
100 功能 4 ,芯片特定功能
101 功能 5 ,芯片特定功能
110 功能 6 ,芯片特定功能
111 功能 7 ,芯片特定功能
6 DSE Drive Strength Enable DSE 驱动力加强设置,此位在各种复用模式下都有效

0 低驱动力模式,如果管脚处于输出模式
1 高驱动力模式,如果管脚处于输出模式
4 PFE Passive Filter Enable 被动滤波功能,此位在各复用状态下都有效

0 关闭被动滤波
1 开启被动滤波,工作在输入状态下,详情参考滤波说明
1 PE Pull Enable PE 使能上下拉电阻

0 无内部上下拉电阻
1 有上下拉电阻
0 PS Pull Select PE 选择上下拉电阻

0 有上拉电阻
1 有下拉电阻

总结

如果要使用某个 GPIO 端口,需要的准备工作是:

  1. 使用 PCC 指针打开对应的 PCCn[] 对应的CGC ,PCCn是 PCC 所指向的结构体内部的数组,固定地址,包含一共有116个 uint32 类型寄存器,将对应的寄存器赋值为 PCC_PCCn_CGC_MASK 即可打开 CGC = 1 。
  2. 设置 GPIO 的控制器中的 PDDR 寄存器,用于调整输入/输出方向。此寄存器在一个类型为 GPIO_Type 的结构中,一共有 5 个固定地址的结构,使用 PTA/PTB/PTC/PTD/PTE 访问。
  3. 设置 PORT.PCR 控制器,关闭中断,MUX 设置成为 001,是否开启被动滤波。使用 PORTA/PORTB/PORTC/PORTD/PORTE 访问。
  4. 读取对应的 PDIR (输入),或者给 PDOR 赋值 (输出)。使用 PTA/PTB/PTC/PTD/PTE 访问。

附录:PCC 各个寄存器地图

偏移地址 寄存器名称 长度/位 (bit) 权限 重启默认值
80h PCC FTFC Register (PCC_FTFC) 32 RW C000_0000h
84h PCC DMAMUX Register (PCC_DMAMUX) 32 RW 8000_0000h
90h PCC FlexCAN0 Register (PCC_FlexCAN0) 32 RW 8000_0000h
94h PCC FlexCAN1 Register (PCC_FlexCAN1) 32 RW 8000_0000h
98h PCC FTM3 Register (PCC_FTM3) 32 RW 8000_0000h
9Ch PCC ADC1 Register (PCC_ADC1) 32 RW 8000_0000h
ACh PCC FlexCAN2 Register (PCC_FlexCAN2) 32 RW 8000_0000h
B0h PCC LPSPI0 Register (PCC_LPSPI0) 32 RW 8000_0000h
B4h PCC LPSPI1 Register (PCC_LPSPI1) 32 RW 8000_0000h
B8h PCC LPSPI2 Register (PCC_LPSPI2) 32 RW 8000_0000h
C4h PCC PDB1 Register (PCC_PDB1) 32 RW 8000_0000h
C8h PCC CRC Register (PCC_CRC) 32 RW 8000_0000h
D8h PCC PDB0 Register (PCC_PDB0) 32 RW 8000_0000h
DCh PCC LPIT Register (PCC_LPIT) 32 RW 8000_0000h
E0h PCC FTM0 Register (PCC_FTM0) 32 RW 8000_0000h
E4h PCC FTM1 Register (PCC_FTM1) 32 RW 8000_0000h
E8h PCC FTM2 Register (PCC_FTM2) 32 RW 8000_0000h
ECh PCC ADC0 Register (PCC_ADC0) 32 RW 8000_0000h
F4h PCC RTC Register (PCC_RTC) 32 RW 8000_0000h
100h PCC LPTMR0 Register (PCC_LPTMR0) 32 RW 8000_0000h
124h PCC PORTA Register (PCC_PORTA) 32 RW 8000_0000h
128h PCC PORTB Register (PCC_PORTB) 32 RW 8000_0000h
12Ch PCC PORTC Register (PCC_PORTC) 32 RW 8000_0000h
130h PCC PORTD Register (PCC_PORTD) 32 RW 8000_0000h
134h PCC PORTE Register (PCC_PORTE) 32 RW 8000_0000h
150h PCC SAI0 Register (PCC_SAI0) 32 RW 8000_0000h
154h PCC SAI1 Register (PCC_SAI1) 32 RW 8000_0000h
168h PCC FlexIO Register (PCC_FlexIO) 32 RW 8000_0000h
184h PCC EWM Register (PCC_EWM) 32 RW 8000_0000h
198h PCC LPI2C0 Register (PCC_LPI2C0) 32 RW 8000_0000h
19Ch PCC LPI2C1 Register (PCC_LPI2C1) 32 RW 8000_0000h
1A8h PCC LPUART0 Register (PCC_LPUART0) 32 RW 8000_0000h
1ACh PCC LPUART1 Register (PCC_LPUART1) 32 RW 8000_0000h
1B0h PCC LPUART2 Register (PCC_LPUART2) 32 RW 8000_0000h
1B8h PCC FTM4 Register (PCC_FTM4) 32 RW 8000_0000h
1BCh PCC FTM5 Register (PCC_FTM5) 32 RW 8000_0000h
1C0h PCC FTM6 Register (PCC_FTM6) 32 RW 8000_0000h
1C4h PCC FTM7 Register (PCC_FTM7) 32 RW 8000_0000h
1CCh PCC CMP0 Register (PCC_CMP0) 32 RW 8000_0000h
1D8h PCC QSPI Register (PCC_QSPI) 32 RW 8000_0000h
1E4h PCC ENET Register (PCC_ENET) 32 RW 8000_0000h

你可能感兴趣的:(S32K144EVB的学习历程(一))