*自写笔记整理,不喜勿喷
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
暂无评论