前言
本文主要介绍了本人在学习使用 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<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 有三个功能:
- 时钟界面开闭控制 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