前言
本文主要介绍了本人在学习使用 S32K144EVB中遇到的问题和解决办法,由于本芯片是 NXP(原 freescale) 生产的基于 ARM M4F 内核的32位芯片,主要适用对象是汽车 。目前在网络上该芯片还没有相关的中文学习资料,到笔者写本文目前,网络上能够找到的资料只有开发板的电路图和 Reference Manual 和该芯片配套的 IDE 内置头文件以及给出的 cookbook 例程,笔者也是在一步步摸索学习,故本文为一个记录性质的文章。
- 本文阅读需要 C 语言基础和一些简单的单片机知识,笔者在之前曾经开发过51单片机和 freescale 公司的 HC08GP32 单片机,故可能会跳过一些基础说明。
- 由于该芯片的 Manual 文件长达 1929 页,全读完肯定要浪费很多时间,为了节约时间,我就针对例程中给出的部分内容查询手册相关内容,进行分析。
- 本文针对 S32K144EVB-Q100X 开发板,但基本原理都是相同的。
Hello World
1. 本例程主要包含以下部分的操作:
- 配置 GPIO
- 根据按键状态输出 LED 灯信号
2. 使用到的电路图:
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<<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 的控制器:
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 端口,需要的准备工作是:
- 使用 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) | 权限 | 重启默认值 |
---|---|---|---|---|
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 |