系列文章目录
【CH32】| 00——开发环境搭建
【CH32】| 01——新建工程 | 下载 | 运行 |调试
【CH32】| 02——常用外设 | GPIO
前言
白嫖了开发板那不得做些事情。现在好多刚入门单片机的都是从库函数入手的 大多教程也是库函数的 这样导致对一些底层怎么实现的不是很清楚。我在学习的时候 老师强烈要求寄存器 然后在学完stm32后 用一些其他单片机也是很容易上手的。所以我就通过带大家看寄存器以及库函数的底层实现深入了解各个外设。
1. GPIO简介
GPIO就是引脚,我们在使用引脚时根据作用 应配置了其模式再去使用。通过看它的参考手册(最好的资料) 了解到GPIO可以配置为很多模式。和stm32一模一样。毕竟抄的 这点可以理解 这样我们再用这个芯片可以无缝上手。当然它也有些自已的特点啦。
2. IO口的内部结构框图
![在这里插入图片描述](https://img-blog.csdnimg.cn/736613e4f4624a95a02739439b54a72c.png)
先简单认识下它内部结构
保护二极管
![在这里插入图片描述](https://img-blog.csdnimg.cn/db2aa241708a433e9fa5f1c5ff6872c2.png)
IO进来之后遇到的第一个就是保护二极管。这两个二极管的作用就是 引脚外部电压过高/过低时 这两个二极管对其进行电压钳位 电压不会流通到后面 从而保护引脚
![在这里插入图片描述](https://img-blog.csdnimg.cn/2ad417eab0074dbe8c07ab1751625308.png)
上下拉电阻
![在这里插入图片描述](https://img-blog.csdnimg.cn/6418d4bedaba4d398fc96f32777d1d5a.png)
这组上拉下拉电阻的作用给IO口配置默认电平 拉高/拉低如果没有上拉/下拉操作 那么这个引脚是出于浮空状态的也就说会受外界因素干扰 。一般单片机复位后大多数IO口都是处于浮空状态的。通过上拉 给IO口一个高电平 ,下拉给IO低电平。让IO口有一个明确的状态。
![在这里插入图片描述](https://img-blog.csdnimg.cn/45a00107573e46509e766c0d8ddc6359.png)
施密特触发器
![在这里插入图片描述](https://img-blog.csdnimg.cn/1d216f47480f47daba118d0d8ce1616c.gif#pic_center)
施密特触发器就是 电压高于某个值 输出变为高电平 低于某个值 输出变为低电平。
两个MOS管
输出模式下有两个Mos管
![在这里插入图片描述](https://img-blog.csdnimg.cn/31656e9e57d44f759e85d545d11e7750.png)
一个PMOS 一个NMOS 他两是让其具有推挽输出和开漏输出的关键。后面详细说。
输出寄存器和输入寄存器
![在这里插入图片描述](https://img-blog.csdnimg.cn/786783af3dad4e10b2b6d9acc16705c1.png)
我们操作单片机 也是通过操作相关寄存器实现相关功能的。寄存器就是我们和机器交互的桥梁 我们不需要管它之后时怎么操作的 只需要配置设置寄存器让他去执行就和。在这里我们通过配置GPIO 输出 和输入 寄存器 来配置IO口模式以及IO输出高低电平。
3. GPIO的几种模式
引脚 无非 输入、输出两大类
3.1 输入
![在这里插入图片描述](https://img-blog.csdnimg.cn/099c2a3fbf44454080bccd5f40b1d8c6.png)
IO配置为输入模式时 是没有复用功能的
模拟输入
模拟输入:一般 ADC采集时 配置为该模式,ADC即采集模拟信号所以 不会经过施密特触发器
![在这里插入图片描述](https://img-blog.csdnimg.cn/dd38455d3fed40be98a3386aa359eb5c.png)
浮空输入
浮空输入:在系统复位后 大多数引脚处于该状态 很容易受外部干扰 这种状态下引脚处于不确定状态 不是高电平也不是低电平 一般测得电压1.几V 还不停变化。
上拉输入
上拉输入:引脚电平拉为高电平(=VDD) 该模式可以确定引脚默认状态 处于高电平 逻辑1 。一般使用在需要检测外部 信号是 低电平有效的情况下 比如 按键检测(低电平有效、某些低电平有效的数字输出传感器)
注意:芯片内部上拉 电流能力很弱 在需要电流高的情况下 外接上拉。
下拉输入
下拉输入: 设置引脚默认电平为 低电平(VSS) 逻辑0 0V
一般用在检测高电平有效的情况下。
3.2 输出
推挽输出
![在这里插入图片描述](https://img-blog.csdnimg.cn/6fe5001e7117440c927babd6c8c456d5.gif#pic_center)
PMOS管相当于NPN三极管 NMOS相当于PNP三极管 动图通过正弦波 模拟高低电平切换时电流的流向。
![在这里插入图片描述](https://img-blog.csdnimg.cn/ccdb285ba21848f6bf367cc91a8896a0.png)
当输出控制 IN输出高电平时,PMOS管导通,NMOS截至 OUT输出高电平。这种情况我们也称为推(灌电流)。
当输出控制 IN输出低电平时,NMOS管导通,PMOS截至 OUT输出低电平。这种情况我们也称为挽(拉电流)。
推挽输出的优点
1、可以快速输出(切换)高低电平
2、驱动能力较强,一般IO口驱动电流可以达到20ma
缺点
不能线与(两个推挽输出IO不能接一起) 推挽时线与等于直接短接VCC和地 会烧了芯片。
推挽输出使用场景:
一般用在需要快速切断的状态 驱动LED 驱动一些数字芯片控制引脚
开漏输出
开漏输出只有一个NMOS管 在工作
![在这里插入图片描述](https://img-blog.csdnimg.cn/0fbed21b54df475b8e2eb2d6e231ee71.png)
当漏输出 通过上拉实现高电平输出能力 输出的高电平=VDD电压 驱动能力和上拉电阻有关。
开漏输出的特点:
可以线与(该模式多个引脚可以连载一起) 可以通过上拉电阻实现 电平转换 电平切换速度也和电阻相关
开漏输出一般用在一些总线信号线比如I2C、电机驱动器信号线等
复用推挽/开漏输出
IO除了本身功能 还可以复用其他功能( 好比我这个IO口 通过复用实现ADC输入引脚的功能等)。在这种复用情况下使用开漏/推挽。
4. GPIO模式如何选择
官方参考手册给出了推荐配置 P52页 我只截取了一部分
![在这里插入图片描述](https://img-blog.csdnimg.cn/5ab248b62288429da4234d29bc2b6dc6.png)
5. 引脚的复用功能
一个IO口除了作为普通引脚 用作输入输出外 还可以有其他功能 我们把这个赋予引脚其他功能的操作叫做复用(重映射) 一个IO口可以复用多个功能
引脚映射有 默认映射 部分 完全三种 不同的映射 让引脚实现不同的作用
![在这里插入图片描述](https://img-blog.csdnimg.cn/e8aff54f5cae4ecf91b333e55f8a75e1.png)
可以看到 复用功能 可以通过 不同的重映射方式 选择好多引脚
其他重映射 P57
6. 引脚产生的中断 | 外部中断
所以IO口都可以配置为外部中断模式。就是将一个中断线 绑定到引脚上 中短线是由编号的 需要和 IO口的对应
比如我 PA1 PB1 PC1 三个端口 他们序号是 1 做外部中断时 是绑定到 中断线1 上的 EXTI1 PC2 即 绑定到EXTI2上的 必须对应。
![在这里插入图片描述](https://img-blog.csdnimg.cn/e8baeb65569e46e293b33904cc7ada8b.png)
7. 引脚的锁定机制
可以让IO口处于锁定状态。无法被修改。只有复位后才可修改。
如何锁定;通过给引脚写1或0完成 锁定时序。 比如:让这个引脚写1 写1 读1 读0 类似
8. GPIO相关寄存器
每个外设的寄存器都有一个基地址(起始地址) 。即这个外设寄存器有固定范围。一个外设有又好几个寄存器 这些是在基地址基础上偏移得到。
我们GPIO也是一样的 不同端口有不同的基地
它的地址在哪看 参考手册
如图该芯片 GPIO 不同端口的基地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/760768c2d0b54749883de8fcb93eb1cd.png)
GPIO相关寄存器名称即地址如下
![在这里插入图片描述](https://img-blog.csdnimg.cn/44c2ac362bb44f85a1c1256408953969.png)
下面我们详细介绍相关寄存器
配置寄存器低位 GPIOx_CFGLR
为什么叫配置寄存器低位 因为我手里这个芯片CHV003 一个端口 只有8个引脚 如PA0-7 一个配置寄存器是32位4字节 负责8个IO 引脚更多的芯片 就有配置寄存器高位 (一个端口 16个IO口)
![在这里插入图片描述](https://img-blog.csdnimg.cn/5b89d90e49184727882c96854ec41ac7.png)
那么具体怎么写呢
eg: 配置PA1为推挽输出 速度为50MHZ
1、可以看到一个IO口由 MODE CNF 一共四位配置 MODE[1:0]这两位在低位
2、配置PA1 A端口引脚 是不是要通过配置A端口的配置寄存器
GPIOA_CFGLR
3、要配置为输出模式 速度50MHZ
MODE和CNF时配套的 可以看到 由8组 序号是从0-7它对应8个引脚0-7
我们是PA1 那么 要设置 CNF1 MODE1 这四位
输出模式 50MHZ MODE1[1:0] =0x3或3; 3 就是二进制11
推挽输出 CNF1[1:0]=0x00或0;二进制00
合在一起 二进制 0011 是不是就是十进制3;
4、将设置值给这个寄存器
GPIOA_CFGLR | = 3<<4; 因为我们是PA1 相当于第二个引脚 一个引脚用4位 所以<<4bit
端口输入寄存器 GPIOx_INDR
相当于 读 获取引脚电平
![在这里插入图片描述](https://img-blog.csdnimg.cn/1ec2ed90afd740fc83e343dd2bd3b729.png)
可以看到 只用到低8位 因为我们芯片一个端口8个IO 每1位对应我们1个IO的电平 IDR0位 对应 编号0的IO的电平
获取引脚电平 通过读引脚对应 该寄存器的位
eg:读 PC6 IO口的电平
if(GPIOC_INDR&(1<<6)==1) 如果GPIOC INDR寄存器第6位为1 则PC6为高电平 反则低电平
端口输出寄存器 GPIOx_OURDR
这个寄存器就是 让这个IO输出 高电平还是低电平 如果IO是输入模式 可以让其设置默认电平为高还是低
![在这里插入图片描述](https://img-blog.csdnimg.cn/6a52cefb047a434eabb0ba6ec471a1a9.png)
eg: 设置PD4 输出高电平
GPIOD_OUTDR|=1<<4; 第四位设置为1 即 PD4输出高电平
端口复位/置位寄存器 GPIOx_BSHR
复位/置位 复位等价于输出低 置位等价于 输出高
可以看到如果 BS BR同时设置了 BS起效即 输出高起效果
![在这里插入图片描述](https://img-blog.csdnimg.cn/e7f8e9e161cd40abae9c856b423c3f2f.png)
eg: PC1 复位(清除/输出低)
GPIOC_BSHR |=1<<17; 复位是设置 BR位
eg: PC1 置位(输出高)
GPIOC_BSHR |=1<<1; 置位是设置 BS位
端口复位寄存器 GPIOx_BCR
和BSHR比较来说 这个只有复位(清除的功能)
![在这里插入图片描述](https://img-blog.csdnimg.cn/3505bb9106864ace82bfbcf72ff38afa.png)
操作和上面是一样的
配置锁存寄存器 GPIOx_LCKR
锁定IO配置的
![在这里插入图片描述](https://img-blog.csdnimg.cn/a395cd5619904ead99b3dcf9e454660f.png)
锁定IO 让其无法被改变 后面代码详细说明
复用相关的寄存器 后面用到时候再说 初步看了这些寄存器 我们开始实操
9. 实践(代码篇)
9.1 点灯前
看查原理图 LED灯的电路 及控制引脚
![在这里插入图片描述](https://img-blog.csdnimg.cn/5d4fae3bb91649d6bc62edbf224959f7.png)
我这里选择通过 PC1控制LED1 所以用杜邦线吧PC1端口和LED1连接起来
9.2 寄存器操作——点灯
1、定义PC端口相关寄存器
通过看手册 我们知道PORT C端口的起始地址
0X400011000
![在这里插入图片描述](https://img-blog.csdnimg.cn/dd89b191e693492db3824e8f0462bb5a.png)
定义C端口 寄存器的地址(基地址)
因为库里已经定义了 GPIOC_BASE这种名字 我这里 后面加个杠
定义C端口 相关寄存器的地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/4c1aefc97356491d9fb07fd938e3b167.png)
或者看具体寄存器描述上 有在基地址基础上的偏移地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/789db622efa747f0b436cde6f8df157c.png)
操作寄存器 相当于操作地址 所以我们需要使用指针
![在这里插入图片描述](https://img-blog.csdnimg.cn/0b4c84e25c9f4fce97b1d360adfd67d6.png)
2、使能时钟 在使用GPIOC 前需要开GPIOC的时钟 这样GPIOC才会有作用
无论使用什么外设 在使用前都需要开相应的时钟 该外设才会生效
![在这里插入图片描述](https://img-blog.csdnimg.cn/5970e28a1f0947df8d7a874f215b30fc.png)
寄存器地址定义方式一样的
![在这里插入图片描述](https://img-blog.csdnimg.cn/1c2f5933b52749e28c7afd012ed82fd1.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/1876c0bc9a3046368bf3e75dee0c5c4c.png)
3、配置IO口模式 配置PC1为推挽输出 50MHZ
![在这里插入图片描述](https://img-blog.csdnimg.cn/3d2b8e225c3045d5b551b4f496c1c0a3.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/a0318e83f7e9443cb76ad3f6b082caf8.png)
4、控制IO口(输出高低电平)
![在这里插入图片描述](https://img-blog.csdnimg.cn/fc76a32ea2ea436290dd9c4f2234050c.png)
5、全部代码
![在这里插入图片描述](https://img-blog.csdnimg.cn/1f0f40a853ad496489a3aebba096d864.png)
6、LED闪烁
![在这里插入图片描述](https://img-blog.csdnimg.cn/4d16e23b5ac346168e62fa742324027d.png)
9.3 库函数操作——点灯
通过上面我们已经对相关寄存器很清楚了 库把寄存器操作封装成了函数 这样方便我们快速使用不用 看手册 查寄存器。
1、我们先看看库是怎么实现的
它也定义了寄存器地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/68d7d1bf1d4f4cf382b2b3248850dc68.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/d788ce2ee6544c76a62e0a07fad088c5.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/f56e1d78458d430390156122ad07c245.png)
可以看到它通过结构体定义了 相关寄存器 然后不同端口的定义 只是将 端口地址给到这个结构体 通过这样的方式 实现不同端口访问相应寄存器 这个操作有C基础肯定可以看到就不说拉
这样情况下 访问寄存器 GPIOC->CFGLR |=3<<4;
接下来我们看看 GPIO相关操作函数是如何实现的
先来看初始化函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/a3d37a45ee8540fdb958ea0d19086a13.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/f85f242fe00f4b36b3d02662fe032cdc.png)
可以看到 底层还是操作寄存器的
大家有兴趣可以 看下其他都是怎么写的
2、点灯
![在这里插入图片描述](https://img-blog.csdnimg.cn/dfca32857c6a45e482b510ae5f703302.png)
如果第一次接触如何你找这些函数
就去对应 .h头文件看函数