预备C语言知识:
一.
1.
#ifndef //#idndef用来判断后面的标识符是否为未定义的。
#elseif
#endif
在嵌入式的系统开发中,某个文件包含几个头文件,而且每个头文件都有可能都定义了同样的宏,使用#ifndef可以有效地防止对该宏的重复定义。此时第一个头文件中定义的宏变为有效定义,其他的头文件中的定义则被忽略。
2.#ifndef指令通常用于防止多次包含同意文件,也就是说,头文件可以采用类似于以下几行的设置:
#ifndef THINGS_H_
#define THINGS_H_
#endif
假设多次定义了同一头文件,当于处理器第一次遇到该包含文件时,THINGS_H_是未定义的,因此程序接着定义THINGS_H_,并处理文件的其余部分,预处理器下一次遇到该文件时,THINGS_H_早已经被定义了,因此预处理器会跳过该文件的其余部分。
3.
这里有一个问题:为什么会包含同一头文件?为什么会用头文件的名字作为#define的定义?
答:许多文件自身包含了其他文件。头文件的有些语句在一个文件中只能出现一次,标准头文件使用#ifndef来避免多次包含。
编译器提供商采用下述方法来解决这个问题,用文件名标识符,并使用大写字母,用下划线来代替字符。
eg;
#ifndef _STDIO_H_
#define _STDIO_H_
#endif
4.
枚举:枚举用来声明代表整数常量的符号名称。
可以通过enum来创建一个新的类型,并指定值。
枚举类型的目的是为了提高程序的可读性(其实也可以用#define来进行声明,但是可读性不高)
枚举型类型可以默认值也可以指定值。
5.
typeof简介:它是一种高级数据格式
typeof unsigned char BYTE
//和
#define BYTE unsigned char
//具有一样的功能
//但是typeof的功能比#define更加的强大:
//eg
typeof char * STRING
STRING NAME,NUM;
//char * NAME,char * NUM;
//和
#define STRING char *
STRING NAME,NUM;
//char * NAME,NUM;
//对结构体也可使用typeof
6.#include “” and #include <>
#include ""//引用的头文件为程序目录的相对路径的头文件,表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找
#include <>//引用的是编译器中类库里面的头文件,使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找
7.
2.对于基于IAR IDE的K60芯片的野火的函数库的PORT端口来说:
有两个文件为:PORT.h和PROT.c
二:下面详细的分析这两个文件:
1.
在PORT端口的定义中,对端口的定义用的枚举类型:
//枚举端口管教
typeof enum
{
/* PTA端口 */ //0~31
PTA0, PTA1, PTA2, PTA3, PTA4, PTA5, PTA6, PTA7, PTA8, PTA9, PTA10, PTA11, PTA12, PTA13, PTA14, PTA15,
PTA16, PTA17, PTA18, PTA19, PTA20, PTA21, PTA22, PTA23, PTA24, PTA25, PTA26, PTA27, PTA28, PTA29, PTA30, PTA31,
/* PTB端口 */ //32~63
PTB0, PTB1, PTB2, PTB3, PTB4, PTB5, PTB6, PTB7, PTB8, PTB9, PTB10, PTB11, PTB12, PTB13, PTB14, PTB15,
PTB16, PTB17, PTB18, PTB19, PTB20, PTB21, PTB22, PTB23, PTB24, PTB25, PTB26, PTB27, PTB28, PTB29, PTB30, PTB31,
/* PTC端口 */
PTC0, PTC1, PTC2, PTC3, PTC4, PTC5, PTC6, PTC7, PTC8, PTC9, PTC10, PTC11, PTC12, PTC13, PTC14, PTC15,
PTC16, PTC17, PTC18, PTC19, PTC20, PTC21, PTC22, PTC23, PTC24, PTC25, PTC26, PTC27, PTC28, PTC29, PTC30, PTC31,
/* PTD端口 */
PTD0, PTD1, PTD2, PTD3, PTD4, PTD5, PTD6, PTD7, PTD8, PTD9, PTD10, PTD11, PTD12, PTD13, PTD14, PTD15,
PTD16, PTD17, PTD18, PTD19, PTD20, PTD21, PTD22, PTD23, PTD24, PTD25, PTD26, PTD27, PTD28, PTD29, PTD30, PTD31,
/* PTE端口 */
PTE0, PTE1, PTE2, PTE3, PTE4, PTE5, PTE6, PTE7, PTE8, PTE9, PTE10, PTE11, PTE12, PTE13, PTE14, PTE15,
PTE16, PTE17, PTE18, PTE19, PTE20, PTE21, PTE22, PTE23, PTE24, PTE25, PTE26, PTE27, PTE28, PTE29, PTE30, PTE31,
}PTXn_e;
//类似于查表来确定端口数值
//枚举端口
typedef enum
{
PTA, PTB, PTC, PTD, PTE,
PTX_MAX,
} PTX_e;
//是为了很好的表示各个端口的基地址
/*! 枚举编号 */
typedef enum
{
PT0 , PT1 , PT2 , PT3 , PT4 , PT5 , PT6 , PT7 ,
PT8 , PT9 , PT10, PT11, PT12, PT13, PT14, PT15,
PT16, PT17, PT18, PT19, PT20, PT21, PT22, PT23,
PT24, PT25, PT26, PT27, PT28, PT29, PT30, PT31,
} PTn_e;
/*! 枚举PORT 配置 */
typedef enum
{
//中断方式和DMA请求方式,两者只能选其中一种(可以不选)
//中断方式选择
IRQ_ZERO = 0x08 << PORT_PCR_IRQC_SHIFT, //低电平触发
IRQ_RISING = 0x09 << PORT_PCR_IRQC_SHIFT, //上升沿触发
IRQ_FALLING = 0x0A << PORT_PCR_IRQC_SHIFT, //下降沿触发
IRQ_EITHER = 0x0B << PORT_PCR_IRQC_SHIFT, //跳变沿触发
IRQ_ONE = 0x0C << PORT_PCR_IRQC_SHIFT, //高电平触发
//DMA请求选择
DMA_RISING = 0x01 << PORT_PCR_IRQC_SHIFT, //上升沿触发
DMA_FALLING = 0x02 << PORT_PCR_IRQC_SHIFT, //下降沿触发
DMA_EITHER = 0x03 << PORT_PCR_IRQC_SHIFT, //跳变沿触发
HDS = 0x01 << PORT_PCR_DSE_SHIFT, //输出高驱动能力
PF = 0x01 << PORT_PCR_PFE_SHIFT, //带无源滤波器
SSR = 0x01 << PORT_PCR_SRE_SHIFT, //输出慢变化率 Slow slew rate
//下拉上拉选择
PULLDOWN = 0x02 << PORT_PCR_PS_SHIFT, //下拉
PULLUP = 0x03 << PORT_PCR_PS_SHIFT, //上拉
//功能复用选择(如果不需要改变功能复用选择,保留原先的功能复用,直接选择ALT0 )
//需要查 K60 Signal Multiplexing and Pin Assignments
ALT0 = 0x00 << PORT_PCR_MUX_SHIFT,
ALT1 = 0x01 << PORT_PCR_MUX_SHIFT, //GPIO 8
ALT2 = 0x02 << PORT_PCR_MUX_SHIFT,
ALT3 = 0x03 << PORT_PCR_MUX_SHIFT,
ALT4 = 0x04 << PORT_PCR_MUX_SHIFT,
ALT5 = 0x05 << PORT_PCR_MUX_SHIFT,
ALT6 = 0x06 << PORT_PCR_MUX_SHIFT,
ALT7 = 0x07 << PORT_PCR_MUX_SHIFT,
} port_cfg;
//枚举PORT复用配置,采用枚举赋值,增加易读性
//其实也可以直接赋值
//一开始并不明白为什么要枚举端口号和枚举编号,枚举了管脚就可以了,头文件看到这,在看一下.C文件
PORT.C
#include "common.h" //山外K60 平台常用类型声明和宏定义
PORT端口中一共有三个函数分别为:
端口初始化和复用函数(配置MUX复用功能):
extern void port_init(PTXn_e(端口号),uint32 cfg);
端口初始化和复用函数(不改变MUX复用功能):
extern void port_init_NoALT(PTXn_e(端口号),uint32 cfg);
外部中断的服务函数:
extern void port_handler(void);
在看PORT.C
#include "commom.h" //类型声明和宏定义
#include "port.h" //自己写的函数
PORT_MemMapPtr PORTX[PTX_MAX] = {PORTA_BASE_PTR, PORTB_BASE_PTR, PORTC_BASE_PTR, PORTD_BASE_PTR, PORTE_BASE_PTR};
//PORT_MemMapPtr为一个结构体指针 ,PORTX[PTX_MAX]这个数组里面存的是各个端口的基地址
//现在懂得在PORT.H中关于枚举端口的意思。
void port_init(PTXn_e ,uint32 cfg)
{
SIM_SCGC5 |= (SIM_SCGC5_PORTA_MASK << PTX(ptxn)); //开启PORTx端口 1.使能PORT时钟
//段地址属于基地址的一种
//4004_8038
//0100 0000 0000 0100 1000 0000 0011 1000
//#define PTX(PTxn(枚举管脚)) ((PTxn)>>5)
//右移5位,/2^5(32)
// SIM_SCGC5_PORTA_MASK = 0x200 0010 0000 0000 默认的开启了PORTA的时钟因为是/32?为什么是32,因为是2的次方数
//因此PTX(ptxn)的为0 1 2 3 4
//因此输入管脚口,就可以进行相应口的使能时钟
PORT_ISFR_REG(PORTX_BASE(ptxn)) = (1<<PTn(ptxn));
// 清空标志位
//每个管脚都有相应的中断标志位
//现在理解枚举端口的含义
//#define PTn(ptxn) ((PTxn)&0x1f) 为的是中断端口的相应管脚,每个端口一共31个口,相应的管脚为1
//PORT_ISFR_REG(PORTX_BASE(ptxn))为指向端口的地址
//这一步操作的是中断状态寄存器
PORT_PCR_REG(PORTX_BASE(PTxn), PTn(PTxn)) = cfg;
//复用引脚功能
//这句话可以复用的功能有:中断触发方式,无源滤波器,复用功能口,上下拉电阻
//操作的寄存器:引脚控制寄存器 n (PORTx_PCRn),每个管脚都有引脚控制寄存器
//PORT_PCR_REG(PORTX_BASE(PTxn), PTn(PTxn))把管脚定义到具体的一个管脚
//cfg根据数据手册进行搭配
//由此K60芯片的管脚以初始化完毕
void port_init_NoALT(PTXn_e ptxn, uint32 cfg)
{
SIM_SCGC5 |= (SIM_SCGC5_PORTA_MASK << PTX(ptxn)); //开启PORTx端口
PORT_ISFR_REG(PORTX_BASE(ptxn)) = (1<<PTn(ptxn)); // 清空标志位
//清空cfg里的MUX,加载寄存器里的MUX
cfg &= ~PORT_PCR_MUX_MASK; //清了MUX 字段(即不需要配置ALT,保持原来的ALT)
cfg |= (PORT_PCR_REG(PORTX_BASE(ptxn), PTn(ptxn)) & PORT_PCR_MUX_MASK); //读取寄存器里配置的 MUX
PORT_PCR_REG(PORTX_BASE(ptxn), PTn(ptxn)) = cfg; // 复用功能 , 确定触发模式 ,开启上拉或下拉电阻
}
}
//端口的外部中断功能:
首先要进行把 porta_handler 函数添加到中断向量表,不需要我们手动调用
操作:
set_vector_handler(PORTA_VECTORn , 中断函数名);
在进行这个函数解析的时候有一个很大的问题。
关于断言的使用:(一定要学好宏定义)
再开一篇····