百度网站前面的图片,北京it培训机构,单页购物网站源码,培训机构seo在WB32F103#xff08;ARM cortex m3内核#xff0c;96Mhz#xff09;的gpio初始化中有一段代码#xff0c;充分的结合了硬件特征并使用C语言的技巧来快速的配置对应的GPIO的功能#xff0c;堪称经典和楷模#xff0c;代码异常简洁#xff0c;执行速度快#xff0c;配置…在WB32F103ARM cortex m3内核96Mhz的gpio初始化中有一段代码充分的结合了硬件特征并使用C语言的技巧来快速的配置对应的GPIO的功能堪称经典和楷模代码异常简洁执行速度快配置任意IO方便快捷。 我们先来看看这一段源代码
void GPIO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint32_t PinConfig)
{uint32_t tmp PinConfig;/* Check the parameters */assert_param(IS_GPIO_ALL_PERIPH(GPIOx));assert_param(IS_GPIO_PIN(GPIO_Pin));GPIOx-CFGMSK ~GPIO_Pin;GPIOx-MODER ((tmp 28) 0x3) * 0x55555555U;GPIOx-OTYPER ((tmp 24) 0x1) * 0xFFFFFFFFU;GPIOx-OSPEEDR ((tmp 20) 0x3) * 0x55555555U;GPIOx-PUPDR ((tmp 16) 0x3) * 0x55555555U;tmp (tmp 0xF) * 0x11111111U;GPIOx-AFRL tmp;GPIOx-AFRH tmp;
}相应的调用方式如下
GPIO_Init(GPIOB, GPIO_Pin_8|GPIO_Pin_9, GPIO_MODE_IN | GPIO_PUPD_UP|GPIO_SPEED_LOW);一条简单的函数调用代码就可以完成对同一组IO的多个相同功能IO的同时配置代码不可谓不经典和优雅高效易读易懂。 但是很多人在阅读GPIO_Init函数的源代码的时候对里面的一些运算操作确感觉到懵懵懂懂理解不了搞不明白。下面我们结合该硬件的特征讲解和说明一下注意不同的MCU的硬件寄存器会有不同的操作方式需要紧密结合你使用的MCU的硬件寄存器的特征来理解可以起到举一反三和触类旁通的作用 要完成这个“一步到位”的操作首先要对相关的宏进行合理的有技巧的定义看看相关IO操作的宏定义如下
/** defgroup GPIO_MODE_define* {*/
#define GPIO_MODE_IN 0x00000000U /*! Input mode */
#define GPIO_MODE_OUT 0x10000000U /*! Output mode */
#define GPIO_MODE_AF 0x20000000U /*! Alternate function mode */
#define GPIO_MODE_ANA 0x30000000U /*! Analog mode */
/*** }*//** defgroup GPIO_OTYPE_define* {*/
#define GPIO_OTYPE_PP 0x00000000U /*! Output push-pull */
#define GPIO_OTYPE_OD 0x01000000U /*! Output open-drain */
/*** }*//** defgroup GPIO_SPEED_define* {*/
#define GPIO_SPEED_LOW 0x00100000U /*! Low speed */
#define GPIO_SPEED_HIGH 0x00000000U /*! High speed */
/*** }*//** defgroup GPIO_PUPD_define* {*/
#define GPIO_PUPD_NOPULL 0x00000000U /*! No pull resistor */
#define GPIO_PUPD_UP 0x00010000U /*! Pull up resistor enabled */
#define GPIO_PUPD_DOWN 0x00020000U /*! Pull down resistor enabled */
/*** }*//** defgroup GPIO_Pin_sources * {*/
#define GPIO_PinSource0 ((uint8_t)0x00)
#define GPIO_PinSource1 ((uint8_t)0x01)
#define GPIO_PinSource2 ((uint8_t)0x02)
#define GPIO_PinSource3 ((uint8_t)0x03)
#define GPIO_PinSource4 ((uint8_t)0x04)
#define GPIO_PinSource5 ((uint8_t)0x05)
#define GPIO_PinSource6 ((uint8_t)0x06)
#define GPIO_PinSource7 ((uint8_t)0x07)
#define GPIO_PinSource8 ((uint8_t)0x08)
#define GPIO_PinSource9 ((uint8_t)0x09)
#define GPIO_PinSource10 ((uint8_t)0x0A)
#define GPIO_PinSource11 ((uint8_t)0x0B)
#define GPIO_PinSource12 ((uint8_t)0x0C)
#define GPIO_PinSource13 ((uint8_t)0x0D)
#define GPIO_PinSource14 ((uint8_t)0x0E)
#define GPIO_PinSource15 ((uint8_t)0x0F)
/*** }*/宏里面分别定义了GPIO的pin引脚序号GPIO_PinSource0-15输入输入模式配置输入输出特殊功能模拟输出类型推挽开漏IO速度高速低速输入电阻配置无上下拉上拉下拉。他们的定义bit位置是经过精心的安排和计算的比如不同的功能定义占用的bit位置不重叠方便进行移位运算和对应的寄存器的操作有一一的对应关系以便于后续代码设计和简化代码的操作。好了准备好这些原材料后我们具体看看代码的实现过程 GPIOx-CFGMSK ~GPIO_Pin;这第一行代码非常关键要明白它的作用要对应的查看mcu的规格书我们发现该mcu有一个操作gpio配置寄存器的特殊功能其说明如下 以上说明再翻译一下这一行代码的作用就是允许后续写其他IO配置寄存器的时候只对本次要配置的gpio的对应bit进行写操作不影响无需配置的其他bit后面代码解释再说明。 对应的寄存器定义如下 好了接下来看看第二行代码的作用和操作技巧 GPIOx-MODER ((tmp 28) 0x3) * 0x55555555U;该代码操作的对象是端口模式寄存器对应的寄存器功能如上图(用一个32bit的寄存器来表示16个io的模式配置每一个io的模式配置位占2bit并且按照顺序排列).
#define GPIO_MODE_IN 0x00000000U /*! Input mode */
#define GPIO_MODE_OUT 0x10000000U /*! Output mode */
#define GPIO_MODE_AF 0x20000000U /*! Alternate function mode */
#define GPIO_MODE_ANA 0x30000000U /*! Analog mode */结合前面模式定义的宏来理解 (tmp 28) 0x3-— 该代码的作用就是取到传入的模式配置数据模式配置定义在最高4个bit所以先右移位28bit然后与3取出来其值其结果可能的数据为0123刚好和寄存器的2个bit的4种组合对应 00 --对应输入模式 01–对应输出模式 10–复用功能模式 11–模拟模式 比较让人疑惑或者难以理解就是后续这个乘以0x55555555U要理解这个作用我们把代码换一种写法来看看
GPIOx-MODER 0x55555555U * ((tmp 28) 0x3);0x55555555U用二进制来看看是什么样子0101 0101 0101 0101 0101 0101 0101 0101 我们结合寄存器的每两个bit表示一个gpio的模式配置来看看也就是对应于每一个gpio的配置位初始值为01如果把这个01和前面的模式值((tmp 28) 0x3)进行运算我们发现得到如下结果 0101 0101 0101 0101 0101 0101 0101 0101 和0相乘结果为0000 0000 0000 0000 0000 0000 0000 0000 所有GPIO的配置bit为输入模式(00) 0101 0101 0101 0101 0101 0101 0101 0101 和1相乘结果不变所有GPIO的配置bit为输出模式(01) 0101 0101 0101 0101 0101 0101 0101 0101 和2相乘相当于左移1位结果为1010 1010 1010 1010 1010 1010 1010 1010 所有GPIO的配置bit为复用功能模式(10) 0101 0101 0101 0101 0101 0101 0101 0101 和3相乘结果为1111 1111 1111 1111 1111 1111 1111 1111 所有GPIO的配置bit为模拟模式(11) 这真是一个非常高效和简洁优雅的设计技巧包括硬件和软件。 关键点来了这个值的修改是对所有16个gpio进行同时操作的如果我只是设置某一个gpio会不会影响到其他gpio的配置呢答案是肯定不会。 回到前面我们看看有一个关键寄存器GPIOx-CFGMSK英文全称应该是config mask中文翻译为配置辅助寄存器直译为配置屏蔽寄存器可能更容易理解一些。 GPIOx-CFGMSK ~GPIO_Pin;通过前面这一条代码的操作屏蔽了不需要操作的gpio配置位也就是说关闭了对无关gpio的bit写的作用比如你本次只是操作gpio0这条代码就会把对gpio1-15的操作屏蔽以后写其他配置寄存器比如前面的MODER寄存器就只有gpio0对应的bit起作用其他bit不会影响原来的值。 接下来的其他几条语句的作用类似参考规格书就可以分析和看明白。 这再一次说明了一个道理嵌入式开发软件和硬件要充分结合才能设计高效的代码。 根据硬件特征设计合理和简洁的操作代码也算是一种算法–一种针对硬件进行操作优化的算法。
文章为原创欢迎转载请注明出处