在讲述寄存器配置之前,我们先来看看二进制中,在不改变其他位的条件下,对某个位的置1和清零。

先看口诀:

清零用&            (对某一位,进行”与”操作。与0相与,会清零。与1相与,不改变)

置1用 |              (对某一位,进行”或”操作。与0相或,不改变。与1相或,会置1)

例如:

二进制:0001 1100,我想要在不改变其他位的情况下,将第四位清零。因为,清零用&,与0相与,会清零,与1相与,不改变。所以:

0001 1100  &   ~(0x01<<3)  ==  0001 0100

注意:0x01<<3  ==  0000 1000(这里由于要和8位的二进制数相与,所以这里会自动扩展成8位)(而且注意这里的移动是指16进制转换成二进制的移动,而不是对16进制进行移动),所以 ~(0x01<<3)  ==  1111 0111, 

所以0001 1100  &   ~(0x01<<3) ==  0001 1100  &  1111 0111 ==  0001 0100

这样,在不改变其他位的情况下,第四位就被清零了。

二进制:0001 0100,我想要在不改变其他位的情况下,将第四位置1。因为,置1用 | ,与0相或,不改变。与1相或,会置1。所以:

0001 0100  |   0x01<<3  ==  0001 1100

注意:0x01<<3  ==  0000 1000(这里由于要和8位的二进制数相与,所以这里会自动扩展成8位)

所以0001 0100  |   0x01<<3 ==  0001 0100  |  0000 1000 ==  0001 1100

这样,在不改变其他位的情况下,第四位就被置1了。

上述是某一位的改变,但是如果我想一下清零四个位呢?那我们就用0x0f来进行左移

如:0x0f << 4 == 0000 1111 << 4  ==  1111 0000

所以,1011 0100  &   ~(0x0f<<4)   ==  1011 0100  &  0000 1111  ==  0000 0100

前面的四位一下就被清零了。

如何读取寄存器中某个位的值?

上面我们讲述了如何置位/清零寄存器中的值,现在我们来看如何读取寄存器中的值

假设某寄存器的值为:0000011010,这串数据被关在黑黑的寄存器中,我又没开天眼,我要怎样才能知道寄存器中的第4位(注意:第一个数为第0位)是1还是0?

又上面可知,与0相与,会清零。与1相与,不改变。

所以:frac{0000011010}{0000010000}   0000011010与0000010000进行”&”操作,如果结果为真,则0000010000中的第4位就为1,如果结果为假,则0000011010中的第4位就为0

就像这样:frac{0000011010}{0000100000} 这两个数相与,一定为0,即:为假

   

注意:frac{0000011010}{0000010000}   0000011010与0000010000进行”&”操作,其结果是为真

而不是为1,因为”&”操作得出的结果为0000010000,0000010000不等于1,而是等于16

程序:do{

                temp = GPIOA_CRL;      

          }while( ! (temp & (1<<16)))

该程序是读取GPIOA_CRL中的第16位是1还是0,如果是1,则为真。由于非 “!” 的存在,所以 ! (temp & (1<<16))为假,所以此时do.....while循环不再循环下去。注意:这里需要用do....while而不用while,是因为需要先执行temp = GPIOA_CRL; 将GPIOA_CRL寄存器中的值放在变量temp中,然后再对temp进行真假判断。

下面我们来看I/O口输出模式的寄存器配置:

1.端口配置低寄存器

注意:在寄存器配置的时候,我们都是先将对应的位清零后,再置1。

端口配置低位寄存器是用来配置某一组I/O的P0~P7引脚(一组I/O有16个I/O口)的I/O口的模式和最大输出速度的。

其中MODE是用来配置输出模式的最大输出速度的,而CNF是用来配置I/O口的工作模式的。所以,该寄存器是每4个位 配置一个I/O口。

输出速度:   

1,最大速度—-引脚上的电平最大翻转速度

2,频率和周期是互为倒数的关系

        1MHZ=1000000Hz 

        1s=1000Ms=10000000us

I/O口输出模式下有三种输出速度可选(2MHz,10MHz,50MHz),这个速度是指I/O⼝驱动电路的响应速度;I/O管脚内部有多个响应

不同的驱动电路,⽤户可以根据⾃⼰的需要选择合适的驱动电路。

⾼低频⽐较 

⾼频驱动电路:输出频率⾼,噪⾳⼤,功耗⾼,电磁⼲扰强; 

低频驱动电路:输出频率低,噪⾳⼩,功耗低,电磁⼲扰弱;提⾼系统EMI(电磁⼲扰)性能;

总结:通过选择速度来选择不同的输出驱动模块,达到最佳的噪⾳控制和降低功耗的⽬的如果需要选择较⾼频率信号,但是却选择了低频驱

动模块,很有可能会失真的输出信号;所以GPIO的引脚速度应与应⽤匹配。

举⼏个栗⼦: 

1. 对于串⼝来说,加⼊最⼤波特率为115200,这样只需要⽤2M的GPIO的引脚速度就可以了,省电噪⾳⼜⼩; 

2. 对于I2C接⼝,假如使⽤400 000波特率,若想把余量留⼤⼀些,2M的GPIO引脚速度或许是不够,这时可以选⽤10M的GPIO引脚速度; 

3. 对于SPI接⼝,假如使⽤18M或9M的波特率,⽤10M的GPIO⼝也不够⽤了,需要选择呢50M的GPIO引脚速度 

4. GPIO⼝设置为输⼊时,输出驱动电路与端⼝是断开的,所以这时配置输出速度是⽆意义的; 

5. 在复位期间和刚复位后,复位功能未开启,I/O端⼝被配置成浮空输⼊模式; 

6. 所有端⼝都有外部中断能⼒,当使⽤外部中断功能时,端⼝必须设置成输⼊模式; 

7. GPIO的配置具有上锁的功能,当配置好GPIO后,可以通过程序锁住配置组合,知道下次芯⽚复位才能解开;

图中的x表示,这个寄存器可以对GPIOA~GPIOE I/O组的I/O口进行配置。

 

        

寄存器中有一个偏移地址:

我们大家都知道有一个寻址方式叫: 基地址 + 偏移地址 寻址。STM32中就是通过基地址 + 偏移地址寻找方式来找到对应的寄存器的。

这个偏移地址表示的是在某I/O组中,该寄存器的首地址,就是以I/O口组中的基地址为标准,加上偏移量得到的。我们假设是GPIOA组,从图中我们知道GPIOA组的基地址为0x4001 0800,而端口配置低位寄存器的偏移量为0x00,所以基地址 + 偏移地址 = 0x4001 0800 +  0x00 = 0x4001 0800,这个0x4001 0800就是端口配置低位寄存器的首地址。

寄存器中还有一个复位值:

        复位值,指的是:在单片机复位后,该寄存器中被默认初始化的值。图中,该寄存器的复位值是0x4444 4444 ,所以在复位后,该寄存器被默认赋值为0x4444 4444。

我们现在给GPIOC组中的I/O口Pin7配置为推挽输出模式,输出速度为2MHZ:

        清零用&,与0相与,会清零,与1相与,不改变。 

        置1用|,与0相或,不改变。与1相或,会置1

         GPIOC->CRL  &= ~( (uint32_t) 0xf << 28 );       //将28~31位清零

        GPIOC->CRL |= 0x2 << 28;        //设置I/O口模式

        即:1111 << 28 == 1111 0000 0000 0000 0000 0000 0000 0000,所以 ~(0xf << 28 ) == 0000 1111 1111 1111 1111 1111 1111 1111

注意:这里要加上(uint32_t) 写成~( (uint32_t) 0xf << 28 ),否则会报警告 warning: #61-D: integer operation result is out of range

因为编译器默认signed int 即32位有符号整数类型,而0xf << 28实际为0xf0000000,这样就有可能改写了符号位(最高位)。所以,我们将其强转为无符号整数类型uint32_t

2.端口配置高寄存器

端口配置高位寄存器是用来配置某一组I/O的P8~P15引脚(一组I/O有16个I/O口)的I/O口的模式和输出速度的。

3.端口输入数据寄存器

 

        

4.端口输出数据寄存器

 

端口输出数据一共有16位,分别对应GPIO组中的Pin0~Pin15,写1则对应的I/O口输出1,写0则I/O口输出0。注意:设置/清除的意思就是置1/清零。

如:GPIOC->ODR |= 0x111 << 6;

即:使GPIOC组中的Pin6~Pin8都输出高电平。

5.端口位设置/清除寄存器

     

端口位设置/清除寄存器的功能和ODR寄存器的功能一样,都是为了设置I/O口的输出电平。实际上往端口位设置/清除寄存器写1/0后,端口位设置/清除寄存器就会往ODR寄存器对应的位写1/0,所以对端口位设置/清除寄存器操作,就是间接的对ODR寄存器进行操作。

注意:我们由上述可以知道,每个I/O口都有上面所描述的寄存器组。

寄存器版——点灯函数:

void LED_Config(void)        //初始化函数,配置GPIOC的PC6~PC8为通用推挽输出模式,输出速度为2Mhz

{

    RCC->APB2ENR |=0X1<<4;        //打开GPIOC时钟

    GPIOC->CRH &= ~0xf;                //设置PC8

    GPIOC->CRH |= 0x1<<1;

    GPIOC->CRL &= ~((uint32_t)0xff<<24);        //设置PC6与PC7

    GPIOC->CRL |= 0x22<<24;

    GPIOC->ODR |= 0x7 << 6;    //关灯  

    

}

void LED_Open(void)

{

    GPIOC->ODR &= ~(0x7 << 6);     //开灯,即I/O口输出低电平

}

void LED_Close(void)

{

    GPIOC->ODR |= 0x7 << 6;    //关灯,即I/O口输出高电平    

    

}

关键字:       编辑:什么鱼 引用地址: