/* 外设基地址 */
#define PERIPH_BASE 0x40000000UL
/* 总线基地址 */
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL)
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL)
#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000UL)
/* GPIO 外设基地址 */
#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000UL)
#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400UL)
#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800UL)
#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00UL)
#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000UL)
#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400UL)
#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800UL)
#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00UL)
#define GPIOI_BASE (AHB1PERIPH_BASE + 0x2000UL)
#define GPIOJ_BASE (AHB1PERIPH_BASE + 0x2400UL)
#define GPIOK_BASE (AHB1PERIPH_BASE + 0x2800UL)
数字後面的"UL"是後缀,代表unsigned longlong
这些地址在前几天都已经介绍过了,现在看来应该熟悉多了,相同类型的地址都是以一个基地址去做偏移。
typedef struct
{
__IO uint32_t MODER; /*!< GPIO 模式暂存器 Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO 输出类型暂存器 Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO 输出速度暂存器 Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO 上拉/下拉暂存器 Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO 输入数据暂存器 Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO 输出数据暂存器 Address offset: 0x14 */
__IO uint32_t BSRR; /*!< GPIO 置位/复位暂存器 Address offset: 0x18 */
__IO uint32_t LCKR; /*!< GPIO 配置锁定暂存器 Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO 富用功能配置暂存器 Address offset: 0x20-0x24 */
} GPIO_TypeDef;
这样我们就可以以结构体的方式来使用暂存器,增加可读性。
最後再来简化一下名称,以GPIOx的方式直接获取GPIOx的基位址。
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
#define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)
#define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE)
#define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
stm32几乎都把所有参数都用define的方式来定义增加可读性,我们再附上GPIO_Pin,以及GPIO_PinState的定义吧
#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 */
可以发现GPIO_PIN_0所对应到的数字为0x0001转换为2进位的话就是0b 0000 0000 0000 0001,我们再来做几个转换,观察一下
GPIO_PIN_1 对应到0x0002 = 0b 0000 0000 0000 0010
GPIO_PIN_2 对应到0x0004 = 0b 0000 0000 0000 0100
GPIO_PIN_3 对应到0x0008 = 0b 0000 0000 0000 1000
GPIO_PIN_4 对应到0x0010 = 0b 0000 0000 0001 0000
这样应该很清楚了吧,一次就是只有一个bit会是1,这样就可以用每个位元来代表pin0~15刚好16个位元!
而最後一个GPIO_PIN_ALL 对应到0xffff转换成二进位就是每个位元都是1喔
接着是GPIO_PinState的部分:
typedef enum
{
GPIO_PIN_RESET = 0,
GPIO_PIN_SET
}GPIO_PinState;
这里比较特别,是用C语言列举的语法(enum)事实上他与define没什麽不同啦,如果不太认识的可以再翻一下C语言的相关书籍。总之GPIO_PIN_RESET代表0,而GPIO_PIN_SET代表1(enum的会照着顺序定义下去,可以省略不写)
之前在使用GPIO输出高电位的时候曾经见过这个函式,但当时我们没有对这个函式的API做进一步的讲解,经过我们好几天对於暂存器的介绍,现在来好好地看看这个函式吧
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if(PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;
}
}
而当我们要使用这个函式的时候会打
HAL_GPIO_WritePIn(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
虽然一开始要记得这些变数名称不是很容易,也会觉得相比Arudino的digitatWrite()的语法难上许多,但是这可是stm32经过暂存器封装後的结果!要是不这麽打,我们就要自己去操纵那些暂存器,并且还要记得许多暂存器的基位址与偏移位址,因此这样的打法现在看来是不是更清楚了呢。
这个函式一开始的assert_param只是要确定传进来的参数是符合的我们要求的,可以先忽略,底下if-else才是真正实际操纵暂存器的部分,至於实际上的运作原理就交给大家思考罗~(可以再回去看一下昨天对於BSRR暂存器的介绍)
>>: 【从实作学习ASP.NET Core】Day13 | 後台 | 编辑与删除
今天继续认识四种 Promise 可以使用的方法,基础的用法可以先参考昨天的文章 Promise.a...
NACL vs SG 的安全设定介绍 当请求想进出在 Private Subnet 内的 EC2 ...
昨天我们了解了游戏执行区以及场景编辑区中"Main Camera"物件对其的影响...
今天,我们来学位元运算子以及赋值运算子吧! 位元运算子 位元运算子作用於位元,并逐位执行。"&...
上一回,我提到 CC: Tweaked 的 Computer 方块有许多基础指令 但我不打算逐一介绍...