STM32F103-IO配置打包函数

STM32 IO配置优化
本文探讨了STM32单片机中IO配置的优化方法,通过分析官方库的IO配置函数,提出了减少代码冗余的自定义配置函数。该函数仅需输入引脚位置和模式,自动配置时钟源等参数,简化了编程流程。

因为我们在编写代码的时候,往往要根据不同功能的IO进行不同的参数配置,虽然,官方给出了固件库,能够直接复制进行IO配置,但是,对于众多IO进行不同参数配置时,一味重复的复制这种长的代码,不但浪费时间,而且还使得代码文件字母感觉密密麻麻的,,其实根据IO配置时,无非我们通常要配置的是它们的IO模式,IO位置以及偶尔配置引脚频率而已,而其它那些东西并不是我们需要重复编写的,所以,根据需求,我们可以自己根据官方库来打包一个IO配置的函数,根据上述的配置要求,我们希望的是,我们只需要输入,要配置的引脚位置(IO的字母位置+IO的数字位置)和引脚模式(频率往往默认配置成50M)就能将对应IO配置完成即可。
那么,首先分析官方库提供的IO配置函数的要求:
    *IO官方配置函数{
        IO结构体开启
        --1.引脚时钟开启(时钟源,使能控制)
        --2.引脚位置指定(引脚位置)
        --3.引脚频率配置(引脚频率)
        --4.引脚模式配置(引脚模式)
        IO结构体关闭,并且自动使能上述配置
        }
根据上述的分析,我们可知,我们需要起码5个参数的配置
    -时钟源
    -使能控制
    -引脚位置-IO的字母位置
    -引脚位置-O的数字位置
    -引脚频率
    -引脚模式
而根据我们的需求,我们只想要输入三个参数,那么也就是说,我们得想办法将那几个无意义的参数进行自动配置,那么自动配置的实际实现方法有两种,一种就是设定成默认值,另外一种就是考虑我们需要的参数和无意义的参数是否可以找到一种唯一关系(相当于高数中的映射中的单射),一旦输入某个参数,就能计算出另一个参数。
    *IO的字母位置
        -数据类型:GPIO_TypeDef *   (Pointer类型)
        -值的范围:GPIOA=0x40010800;GPIOB=0x40010C00;GPIOC=0x40011000;~~GPIOG=0x40012000;(通过KEIL仿真中的Watch标记GPIOA即可),因为我们关注两个参数变动时,往往关注它变动的部分,我们往往从会变动的最低位作为观察的底位,变动的最高位作为观察的顶位,其实从A~G变动的只是数值中的第三位和第四位,(从0x的角度看)它的数值变动相当于从0x08开始,每次数值+4;最终值为0x20(从bit的角度看)它的数值变动相当于从0b0010开始,每次数值+1,最终值为0b_1000,

    *IO的数字位置
        -数据类型:u16
        -值的范围:GPIO_Pin_0=0x0001;GPIO_Pin_1=0x0002;GPIO_Pin_2=0x0004;~~GPIO_Pin_15=0x8000;而变动的顺序是每引脚数字位置上升一个,数值左移一位(从bit的角度看)。

    *时钟源
        -数据类型:u32
        -值的范围:RCC_APB2Periph_GPIOA=0x00000004 ;RCC_APB2Periph_GPIOB=0x00000008;~~RCC_APB2Periph_GPIOG=0x00000100;
也就是说对于这个参数,相当于从RCC_APB2Periph_GPIOA=4开始,而变动的顺序是每字母上升一个,数值左移一位(从bit的角度看)。

    *使能控制
        -数据类型:实际上就是bool
        -值的范围:DISABLE = 0;ENABLE =!DISABLE (实际上就是“1”),而对于这个参数,在这地方没什么意义,因为一旦配置引脚,往往目的是让引脚使能的,而不是配置了却不让它关着不用。

    *引脚频率
        -数据类型:u8
        -值的范围:未知,在Watch找不出,而且,对于这参数而言,往往我们默认设置它为50MHZ即可,如果没有特殊要求不需要去修改它。

    *引脚模式
        -数据类型:u8
        -值的范围:不存在一个数值对应关系,它的值在文件"stm32f10x_gpio.h"中被列出,而且这是我们想要进行提供配置的参数。

根据上述的参数分析,首先可以明确的是,参数“引脚频率”和“使能控制”可以通过默认的方式进行省略,那么还剩下一个时钟源参数,因为时钟源的配置,对应的下标其实就是IO的字母位置,所以它们俩应该可以通过某种数字对应关系,(就好比y=f(x)的关系,而x对应的是“IO的字母位置”,y对应的是“时钟源”),所以根据上述的“IO的字母位置”(bit角度看)每次累加1,和“时钟源”(bit角度看)每次数值左移一位。那么就可以很容易想到,把“IO的字母位置”的初始值通过减法的方式变成0,然后,根据累加几次,就移几位的法则,来实现这两个变量的对应,从而得到输入一个参数控制两个参数的效果。不过这里存在一个问题,就是两个参数的数据类型不同,“IO的字母位置”的数据类型是“指针”,而“时钟源”的数据类型是“u32”,因为,“时钟源”是y,也就是说,它才是目标,而“IO的字母位置”只是一个原始数据,所以,我们不需要考虑怎么转化指针变量,只需要提取出它具体的数据即可,然后根据计算公式还原成u32类型即可,具体的计算方法如下:

    *1.明确x(“IO的字母位置”)的用处
        -它是用来控制y(“时钟源”)的移位个数的。

    *2.明确y(“时钟源”)的初始值
        -“时钟源”的初始值=0x00000004;

    *3.提取x(“IO的字母位置”)中的有效数据
        -根据x的用处,当“IO的字母位置”为“A”时应该为“0”,为“B”时应该为“1”....也就是每次字母上升一个,数据就累加1个,所以,第一步是将这个指数类型的数据,先将其数值提取出来,然后提取出它的有效的数据。

 

    *4.明确(“IO的字母位置”)中的有效数据 与 移位个数的关系
        -因为根据上述的有效数据的提取结果后,A=2,B=3,C=4,....而我们需要的是“A”时应该为“0”,为“B”时应该为“1”....也就是目标值和提取值只是简单的差一个减法关系,所以人为的添加一个减法量即可,也就是“移位个数”=“(“IO的字母位置”)中的有效数据”-“2”。

    *5.运算出结果后还原当前的“时钟源”的数据类型,并应用
        -计算出来的结果虽然和当前的“时钟源”参数的值相同,但是他计算结果(u16)和“时钟源”(u32)的数据类型不同,而对于这种缺少位数而且缺少的位数均为“0”的问题,可以通过强制转换类型来直接补充即可,即可实现自动输出参数“时钟源”。

最终的代码案例:

#include <M_all.h>  

void GPIO_Pin_States_Set(GPIO_TypeDef * m_GPIOx,u16 m_GPIO_Pin_x,u8 m_Mode)
{
	GPIO_InitTypeDef GPIO_InitStructure;                                                       //结构体_引脚-声明
	
	RCC_APB2PeriphClockCmd(((u32)4<<((u8)(((u16)m_GPIOx)>>10)-2)),ENABLE);                                               //时钟开启_IO
	
  GPIO_InitStructure.GPIO_Pin = m_GPIO_Pin_x;                                                //结构体_引脚-P

	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                          //结构体_引脚-引脚频率_50Mhz
	
	GPIO_InitStructure.GPIO_Mode = m_Mode;                                                     //结构体_引脚-引脚模式_复用推挽输出
	
	GPIO_Init(m_GPIOx, &GPIO_InitStructure);                                             //结构体_引脚-结束配置
}

 

常用的五个函数(I/O) 1. 图像载入函数 函数cvLoadImage载入指定图像文件,并返回指向该文件的IplImage指针。函数支持bmp、jpg、 png、 tiff等格式的图像。其函数原型如下: IplImage* cvLoadImage( const char* filename, int iscolor); 其中,filename 是待载入图像的名称,包括图像的扩展名;iscolor是一个辅助参数项,可选正数、零和负数三种值,正数表示作为三通道图像载入,零表示该图像作为单通道图像,负数表示载入图像的通道数由图像文件自身决定。 2. 窗口定义函数 函数cvNamedWindow定义一个窗口,用于显示图像。其函数原型如下: int cvNamedWindow( const char* name, unsigned long flags ); 其中,name是窗口名,flags是窗口属性指标值,可以选择CV_WINDOW_AUTOSIZE和0两种值。CV_WINDOW_AUTOSIZE表示窗口尺寸与图像原始尺寸相同,0表示以固定的窗口尺寸显示图像。 函数 cvDestroyWindow(const char* name);销毁以上定义的窗口。 name是窗口名 3. 图像显示函数 函数cvShowImage是在指定的窗口中显示图像,其函数原型如下: void cvShowImage( const char* name, const CvArr* image ); 其中,name是窗口名称,image是图像类型指针,一般是IplImage指针。 4. 图像保存函数 函数cvSaveImage以指定的文件名保存IplImage类型的指针变量,其函数原型如下: int cvSaveImage( const char* filename, const CvArr* image ); 其中,filename是图像保存路径和名称,image是IplImage指针变量。 5. 图像销毁函数 函数cvReleaseImage销毁已定义的IplImage指针变量,释放占用内存空间。其函数原型如下: void cvReleaseImage( IplImage** image ); 其中,image为已定义的IplImage指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值