今天是学习有方机器人课程的第二天,通过课程复习GPIO相关知识点,很有收获,老师讲的非常好!
处理器控制硬件原理
处理器在日常工作时有两种功能,第一个是负责基本的数据运算和逻辑运算 ,第二是控制硬件外设。
对于处理器来说,正常工作时需要去运行程序,而处理器本身只能运行程序,不能存储程序,程序是存储在内部的**只读存储器(ROM)**中,处理器如何执行程序呢?从ROM里读一条执行一条程序,程序中涉及到的运算就通过RSV来执行,运算结果的数据就存在了RAM里面;
RAM和ROM最大的区别就是RAM掉电后就丢失数据,ROM掉电后不丢失,下次上电后依旧运行之前的程序。
RAM:随机存取存储器(random access memory),又称作"随机存储器",是与CPU直接交换数据的内部存储器,也叫主存(内存)。
ROM:只读存储器(Read Only Memory),是一种只能读出事先所存的数据的固态半导体存储器。ROM中所存数据稳定,一旦存储数据就再也无法将之改变或者删除,断电后所存数据也不会消失。其结构简单,因而常用于存储各种固化程序和数据。
处理器不只是简单的进行数据运算操作,还要进行复杂的控制硬件外设操作
处理器如何控制硬件外设呢?由于CPU 本身是不能直接控制硬件的,硬件一般由其对应的控制器来控制,每一个硬件控制器内部都有其自己的寄存器,开发人员通过向某一硬件控制器内部的寄存器写入特定的值配置该寄存器,依次到达控制该硬件控制器的目的。
在处理器中将各个硬件控制器映射到了 CPU 地址空间中的一段范围,这样 CPU 就可以通过读写寄存器来间接控制硬件。通过IO来实现。比如开发人员想要控制GPIO,需要往GPIO内部寄存器中写入特定的值来配置寄存器,配置好后硬件就会实现对应的功能。
硬件寄存器的本质就是存储器,把开发人员写入的值存在里面,这样硬件就会按照写入值得寄存器的功能执行相应的工作,开发人员通过写入或读取相应寄存器实现数据交互过程,相当于与单片机进行"对话"。
对于处理器来讲,读取存储器里的数据有一个范围,这个范围就是由处理器内部的地址空间决定,对于RAM32位处理器来说,它的读写的范围就是2的32次方=4G,即地址空间大小=4G。
当我们要控制某一外设时,往对应寄存器中写入一个值就可以,以上就是处理器控制硬件原理。
地址映射表
处理器分配地址空间的这一过程就叫做映射 ,把相应外设寄存器对应的地址映射到处理器的内部地址空间中。在一个处理器中,一般会将 ROM、RAM、寄存器存储设备分别映射到寻址空间中的不
同地址段,我们将这个映射关系称为处理器的地址映射表。简单的来说就是处理器读取程序就去 ROM 对应的地址段读取,存储数据就到 RAM 对应的地址段存储,控制硬件控制器就到其内部的寄存器所对应的地址段进行读写操作 ,而 ROM、RAM、寄存器这三者的地址段会形成一张表,将其称为处理器的地址映射表。
启动模式
程序通过ST-Link下载到ROM里,启动模式为主闪存存储模式。
STM32F10X.H 头文件
STM32F10X 系列单片机内部的寄存器数量多且复杂,而此文件是对 STM32 寄存器地址、结构体类型定义的底层头文件,由ST 公司提供,使用 STM32 库时都要包含该文件。
GPIO 的输入输出
通过参考手册,我们知道GPIO的内部结构如下。
输出:
开漏模式下,输出驱动器的P-MOS管失效,当写入0到对应寄存器时,输出控制会响应寄存器里的配置,由于N-MOS管导通,IO引脚被拉低;当写入1时,由于P-MOS失效,呈现高阻态,需要外接上拉电阻才能把IO引脚拉高。低电平有驱动能力,高电平呈高阻态无驱动能力。
推挽模式下,P-MOS和N-MOS都是有效的。写入1,IO引脚拉高;写入0,IO引脚拉低。低电平和高电平都有效有驱动能力
输入:
模拟输入不经过TTL施密特触发器,施密特触发器本质上来讲就是用来滤波,当信号高于上限值就转换为高电平,低于下限值为低电平。
复用功能输入通过TTL施密特触发器,达到一个滤波的作用。
IO引脚处的两个二极管,起到保护电路的作用。
代码部分(知识点查漏补充)
1、 在对应外设的.h文件里,能找到该外设所有封装函数声明。
cpp
/**
* @brief Writes data to the specified GPIO data port.
* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.
* @param PortVal: specifies the value to be written to the port output data register.
* @retval None
*/
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
{
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
GPIOx->ODR = PortVal;
}
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
{
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_BIT_ACTION(BitVal));
if (BitVal != Bit_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BRR = GPIO_Pin;
}
}
GPIO_Write,是控制GPIOx的所有引脚;GPIO_Write(GPIOA, 0xfeff),表示ODR寄存器的第8位写0,其他15位都为1。
GPIO_WriteBit,是设置GPIOx中某一个引脚的置位或清除,单独对某一个IO口进行配置。
2、可以用GPIO_WriteBit 代替GPIO_SetBits或GPIO_ResetBits。
通过assert_param(),里面的参数,双击选中之后按F12,进行跳转,可以看到对应寄存器的操作。
cpp
/**
* @brief Sets the selected data port bits.
* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.
* @param GPIO_Pin: specifies the port bits to be written.
* This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
* @retval None
*/
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BSRR = GPIO_Pin;
}
/**
* @brief Clears the selected data port bits.
* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.
* @param GPIO_Pin: specifies the port bits to be written.
* This parameter can be any combination of GPIO_Pin_x where x can be (0..15).
* @retval None
*/
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BRR = GPIO_Pin;
}
3、 GPIO_Pin_All代表对所有的引脚都进行定义。
cpp
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
4、当端口需要复用时,要配置复用功能,并开启AFIO时钟。
cpp
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState)
/*
* @brief 表示改变指定引脚的映射,即复用功能
@param GPIO_Remap: 选择要重新映射的引脚。
@param NewState: 端口引脚重新映射的新状态。 ENABLE or DISABLE.
*/
今天学习了P2-P6的视频,完成笔记并补充疏漏知识点!
本人博客仅代表个人见解方便记录成长笔记。
若有不足,请指出,感谢您的阅读!