关于STC15系列的IO口如何配置—简单易懂超详细

*自写笔记整理,不喜勿喷

STC15系列(以下统称15系列)的IO口资源配置和STM32(以下统称32)差不多,在使用前都需要对对应IO口进行配置,唯一的区别就是,32的IO口除了配置模式还需要配置翻转速度,而15系列不需要,15系列需要配置的就只有IO模式这一个。

以下以STC15F2K60S2为例来说,这是STC-ISP烧录软件中带的STC15F系列头文件里的内容

sfr P0M0        =   0x94;   //0000,0000 端口0模式寄存器0
sfr P0M1        =   0x93;   //0000,0000 端口0模式寄存器1
sfr P1M0        =   0x92;   //0000,0000 端口1模式寄存器0
sfr P1M1        =   0x91;   //0000,0000 端口1模式寄存器1
sfr P2M0        =   0x96;   //0000,0000 端口2模式寄存器0
sfr P2M1        =   0x95;   //0000,0000 端口2模式寄存器1
sfr P3M0        =   0xB2;   //0000,0000 端口3模式寄存器0
sfr P3M1        =   0xB1;   //0000,0000 端口3模式寄存器1
sfr P4M0        =   0xB4;   //0000,0000 端口4模式寄存器0
sfr P4M1        =   0xB3;   //0000,0000 端口4模式寄存器1
sfr P5M0        =   0xCA;   //0000,0000 端口5模式寄存器0
sfr P5M1        =   0xC9;   //0000,0000 端口5模式寄存器1
sfr P6M0        =   0xCC;   //0000,0000 端口6模式寄存器0
sfr P6M1        =   0xCB;   //0000,0000 端口6模式寄存器1
sfr P7M0        =   0xE2;   //0000,0000 端口7模式寄存器0
sfr P7M1        =   0xE1;   //0000,0000 端口7模式寄存器1

从这里可以看到,15F系列的IO口最多有8个(P0~P7),而相关配置的寄存器分别是PxM0和PxM1这两个。

以下是我在代码里写的笔记截图

接下来就是使用相关的内容。

通常15系列的IO在使用的时候都会先将其初始化为准双向IO口模式,以方便后续使用

void IO_Init()
{
	P0M0 = 0x00;P0M1 = 0x00;  
	P1M0 = 0x00;P1M1 = 0x00;
	P2M0 = 0x00;P2M1 = 0x00;
	P3M0 = 0x00;P3M1 = 0x00;
	P4M0 = 0x00;P4M1 = 0x00;
	P5M0 = 0x00;P5M1 = 0x00;
	P6M0 = 0x00;P6M1 = 0x00;
	P7M0 = 0x00;P7M1 = 0x00;
	P0 = 0x00;P1 = 0x00;P2 = 0x00;P3 = 0x00;
	P4 = 0x00;P5 = 0x00;P6 = 0x00;P7 = 0x00;
}

这个代码一般都会在main函数的最开始使用一次进行IO初始化

其次就是具体对某一个IO进行配置的方法

我所主流的配置方法无非就两个,也是最常用的一个

一、

//例:配置P53口为推挽输出模式
P5M0 |= 0x08;
P5M1 |= 0x00;

这里面有一个细节,就是用的是或等(|=)运算,为的是不影响别的位的值,防止和别的IO口配置冲突。

这样配置算得上是最简单的,但谈不上方便,因为每次配置IO口的时候还得算一下16进制的值,对一些口算不太好的可能不太友好。

所以就有了另一种配置方法

二、

//例:配置P53为推挽输出模式
P5M0 |= (0x01 << 3);
P5M1 |=0x00;

对于C语言接触过移位操作的看懂这个应该问题不大

十六进制的0x01转换成二进制就是0000 0001

把这个二进制左移3位(0x01 << 3)就得到了 0000 1000

在把转换来的值转换成十六进制就是0x08,和第一个的结果是一样的

这样做的好处就是省去了每次配置的时候都要算一下十六进制值,很优雅

但仅仅如此肯定满足不了一些高玩,于是就有人用结构体来做IO配置

//宏定义IO模式的名字和其对应的值
#define	GPIO_PullUp		0	//上拉准双向口
#define	GPIO_HighZ		1	//浮空输入
#define	GPIO_OUT_OD		2	//开漏输出
#define	GPIO_OUT_PP		3	//推挽输出

//宏定义引脚具体的哪个脚
#define	GPIO_Pin_0		0x01	//IO引脚 Px.0
#define	GPIO_Pin_1		0x02	//IO引脚 Px.1
#define	GPIO_Pin_2		0x04	//IO引脚 Px.2
#define	GPIO_Pin_3		0x08	//IO引脚 Px.3
#define	GPIO_Pin_4		0x10	//IO引脚 Px.4
#define	GPIO_Pin_5		0x20	//IO引脚 Px.5
#define	GPIO_Pin_6		0x40	//IO引脚 Px.6
#define	GPIO_Pin_7		0x80	//IO引脚 Px.7
#define	GPIO_Pin_All	0xFF	//IO所有引脚

//宏定义总包括的引脚
#define	GPIO_P0			0        //IO引脚 P0
#define	GPIO_P1			1        //IO引脚 P1
#define	GPIO_P2			2        //IO引脚 P2
#define	GPIO_P3			3        //IO引脚 P3
#define	GPIO_P4			4        //IO引脚 P4
#define	GPIO_P5			5        //IO引脚 P5
#define	GPIO_P6			6        //IO引脚 P6
#define	GPIO_P7			7        //IO引脚 P7

//结构体区域
typedef struct
{
	u8	Mode;		//IO模式
	u8	Pin;		//要设置的端口	
} GPIO_InitTypeDef;

上面只是对结构体进行了定义,下面是将结构体的内容取地址后赋值给相应IO

void GPIO_Inilize(unsigned char GPIO, GPIO_InitTypeDef *GPIOx)
{
	if(GPIO == GPIO_P0)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P0M1 &= ~GPIOx->Pin,	P0M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P0M1 |=  GPIOx->Pin,	P0M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P0M1 |=  GPIOx->Pin,	P0M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P0M1 &= ~GPIOx->Pin,	P0M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P1)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P1M1 &= ~GPIOx->Pin,	P1M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P1M1 |=  GPIOx->Pin,	P1M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P1M1 |=  GPIOx->Pin,	P1M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P1M1 &= ~GPIOx->Pin,	P1M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P2)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P2M1 &= ~GPIOx->Pin,	P2M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P2M1 |=  GPIOx->Pin,	P2M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P2M1 |=  GPIOx->Pin,	P2M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P2M1 &= ~GPIOx->Pin,	P2M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P3)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P3M1 &= ~GPIOx->Pin,	P3M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P3M1 |=  GPIOx->Pin,	P3M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P3M1 |=  GPIOx->Pin,	P3M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P3M1 &= ~GPIOx->Pin,	P3M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P4)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P4M1 &= ~GPIOx->Pin,	P4M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P4M1 |=  GPIOx->Pin,	P4M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P4M1 |=  GPIOx->Pin,	P4M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P4M1 &= ~GPIOx->Pin,	P4M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P5)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P5M1 &= ~GPIOx->Pin,	P5M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P5M1 |=  GPIOx->Pin,	P5M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P5M1 |=  GPIOx->Pin,	P5M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P5M1 &= ~GPIOx->Pin,	P5M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P6)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P6M1 &= ~GPIOx->Pin,	P6M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P6M1 |=  GPIOx->Pin,	P6M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P6M1 |=  GPIOx->Pin,	P6M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P6M1 &= ~GPIOx->Pin,	P6M0 |=  GPIOx->Pin;	 //推挽输出
	}
	if(GPIO == GPIO_P7)
	{
		if(GPIOx->Mode == GPIO_PullUp)		P7M1 &= ~GPIOx->Pin,	P7M0 &= ~GPIOx->Pin;	 //上拉准双向口
		if(GPIOx->Mode == GPIO_HighZ)		P7M1 |=  GPIOx->Pin,	P7M0 &= ~GPIOx->Pin;	 //浮空输入
		if(GPIOx->Mode == GPIO_OUT_OD)		P7M1 |=  GPIOx->Pin,	P7M0 |=  GPIOx->Pin;	 //开漏输出
		if(GPIOx->Mode == GPIO_OUT_PP)		P7M1 &= ~GPIOx->Pin,	P7M0 |=  GPIOx->Pin;	 //推挽输出
	}
}

至此用结构体的方法已经写好了,下面是在main函数中具体的配置方法

//例:配置P53为推挽输出
GPIO_InitTypeDef GPIO_Init;
//以GPIO_InitTypeDef的方式声明GPIO_Init结构体

GPIO_Init.Pin  = GPIO_Pin_3;
//对结构体GPIO_Init中Pin变量进行赋值
//因为是配置P53,所以赋值为GPIO_Pin_3

GPIO_Init.Mode = GPIO_OUT_PP;
//对结构体GPIO_Init中Mode变量进行赋值
//因为是配置为推挽输出,所以复制为GPIO_OUT_PP

GPIO_Inilize(GPIO_P5,&GPIO_Init);
//将设置好的结构体GPIO_Init信息配置给P5口

这样就完成了一个IO口的配置,看得出来跟库函数的32配置IO口的方法,不能说一模一样,也算是八九不离十了。

以上,就是我对15系列IO口配置的相关理解与使用详细

个人认为只要学会第二种IO口配置就行了,方便省心,后续调试代码也好看一些,但第三种方法比较规矩,就算学不会,也得能看懂。

版权声明:本文为CSDN博主「想当程序猿的凯旋大大」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_28145393/article/details/117751988

生成海报
点赞 0

想当程序猿的凯旋大大

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐