在讲述寄存器配置之前,我们先来看看二进制中,在不改变其他位的条件下,对某个位的置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相与,不改变。
所以: 0000011010与0000010000进行”&”操作,如果结果为真,则0000010000中的第4位就为1,如果结果为假,则0000011010中的第4位就为0
就像这样: 这两个数相与,一定为0,即:为假
注意: 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口输出高电平
}
关键字: 编辑:什么鱼 引用地址: